HEX
Server: Apache/2.4.65 (Debian)
System: Linux web6 5.10.0-36-amd64 #1 SMP Debian 5.10.244-1 (2025-09-29) x86_64
User: innocamp (1028)
PHP: 7.4.33
Disabled: pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,pcntl_unshare,
Upload Files
File: /home/gdtrain/public_html/wp-content/plugins/backup-wd/includes/buwd-destination-gdrive.php
<?php
require_once BUWD_DIR . '/vendor/autoload.php';

/**
 *
 */
class Buwd_Destination_GDrive
{
    protected static $instance = null;
    private $gd_client_id;
    private $gd_client_secret;
    private $service;

    /**
     *
     */
    public function __construct()
    {
        $this->info['title'] = __('Transferring archive to Google Drive', 'buwd');
        $this->info['sync_title'] = __('Syncing files to Google Drive', 'buwd');
        $this->info['name'] = __('GDrive', 'buwd');
        $this->info['desc'] = __('', 'buwd');
        $this->info['sync'] = false;
        $this->info['tab_name'] = 'destination-gdrive';

        $this->set_keys();
    }

    public function defaults()
    {
        $defaults = array(
            'gdrivefolder'         => trailingslashit(sanitize_file_name(get_bloginfo('name'))),
            'gdrivefiledelete'     => 15,
            'gdrivefiledeletesync' => '1'
        );

        return $defaults;
    }

    /**
     * @param $job_id
     *
     * @return array
     */
    public function get_options($job_id)
    {
        set_site_transient('buwd_gdrive_jobid', $job_id, HOUR_IN_SECONDS);

        $disabled = '';
        $readonly = false;

        if (!$this->is_keys_setted() && Buwd_Helper::get('tab') == $this->info['tab_name']) {
            echo '<div class="buwd-messages">' . Buwd_Helper::message(sprintf(__('GDrive API: Please configure Google Drive API keys on %s page of Backup WD plugin.', 'buwd'), '<a href="' . admin_url("admin.php?page=buwd_api_keys") . '" target="_blank">API keys</a>'), 'error') . '</div>';
            $disabled = 'buwd-disabled';
            $readonly = 'readonly';
        }


        $gdrivetoken = Buwd_Options::get($job_id, 'gdrivetoken');
        $client = $this->GDrive();
        if (!empty($gdrivetoken)) {
            $client->refreshToken($gdrivetoken['refresh_token']);
            $auth_text = 'Authenticated';
            $auth_class = 'auth';
        } else {
            $auth_text = 'Not Authenticated';
            $auth_class = 'not-auth';
        }

        $options = array(
            'key'    => 'destination-gdrive',
            'title'  => '',
            'fields' => array(
                array(
                    'label'  => __('Authentication', 'buwd'),
                    'header' => __('Login', 'buwd'),
                    'id'     => 'gdriveauth',
                    'name'   => 'gdriveauth',
                    'type'   => 'hidden',
                    'class'  => array(),
                    'hint'   => array(
                        'html' => '<span class="' . $auth_class . '">' . $auth_text . '</span><a  class="gdrive-auth buwd-button ' . $disabled . '" href="' . admin_url("admin-ajax.php") . '?action=buwd-gdrive&job_id=' . $job_id . '" >Reautenticate</a><p class="description">Press <b>Authenticate</b> to connect Backup WD to your Google Drive. Use the Google account, where you wish to keep the backup files.</p>'
                    ),
                    'value'  => Buwd_Options::get($job_id, 'gdriveauth'),
                ),
                array(
                    'label'  => __('Folder in Google Drive', 'buwd'),
                    'header' => __('GDrive Backup settings', 'buwd'),
                    'id'     => 'gdrivefolder',
                    'name'   => 'gdrivefolder',
                    'type'   => 'text',
                    'class'  => array(
                        'buwd-large-text',
                    ),
                    'attr'   => array(
                        'readonly' => $readonly
                    ),
                    'value'  => esc_html(Buwd_Options::get($job_id, 'gdrivefolder')),
                    'hint'   => array(
                        'html' => '<p class="description">Set the title of your Google Drive folder, where the backup files will be uploaded.</p>'
                    ),
                ),
                array(
                    'label'      => __('File deletion', 'buwd'),
                    'id'         => 'gdrivefiledelete',
                    'name'       => 'gdrivefiledelete',
                    'type'       => 'number', // to number
                    'class'      => array(
                        'buwd-extra-small-text',
                    ),
                    'attr'       => array(
                        'min' => "0"
                    ),
                    'value'      => esc_html(Buwd_Options::get($job_id, 'gdrivefiledelete')),
                    'attr'       => array(
                        'readonly' => $readonly
                    ),
                    'visibility' => Buwd_Options::get($job_id, 'backup_type') == 'archive' ? true : false,
                    'hint'       => array(
                        'html' => '<span>Number of files to keep in folder.</span><p class="description">Specify the maximum number of files in Google Drive folder. When the limit is reached, the oldest backup file will be deleted.</p>'
                    ),
                ),
                array(
                    'label'      => __('File deletion', 'buwd'),
                    'id'         => 'gdrivefiledeletesync',
                    'name'       => 'gdrivefiledeletesync',
                    'type'       => 'checkbox', // to number
                    'class'      => array(),
                    'choices'    => array(
                        '1' => 'Keep deleted files from previous backup sync.'
                    ),
                    'attr'       => array(
                        str_replace('buwd-', '', $disabled) => str_replace('buwd-', '', $disabled)
                    ),
                    'value'      => Buwd_Options::get($job_id, 'gdrivefiledeletesync'),
                    'visibility' => Buwd_Options::get($job_id, 'backup_type') != 'archive' ? true : false,
                    'hint'       => array(
                        'html' => '<p class="description">Uncheck the option to remove the deleted files.</p>'
                    ),
                ),
            ),
        );

        return $options;
    }

    public function set_keys()
    {
        $this->gd_client_id = Buwd_Options::getSetting('gd_client_id');
        $this->gd_client_secret = Buwd_Encrypt::decrypt(Buwd_Options::getSetting('gd_client_secret'));
    }

    public function is_keys_setted()
    {
        if (empty($this->gd_client_id) || empty($this->gd_client_secret)) {
            return false;
        }

        return true;
    }

    public function run_ajax()
    {
        $job_id = get_site_transient('buwd_gdrive_jobid');

        $redirectUri = admin_url('admin-ajax.php') . '?action=buwd-gdrive';
        $config = array('approval_prompt' => 'force');
        //	$config      = array();
        $client = $this->GDrive($config, $redirectUri);

        $redirect = array(
            'page'   => 'buwd_editjob',
            'job_id' => $job_id,
            'tab'    => 'destination-gdrive',
        );

        if ($job_id) {
            if (isset($_GET['code'])) {
                try {
                    $client->fetchAccessTokenWithAuthCode($_GET['code']);
                    $access_token = $client->getAccessToken();

                    if (!empty($access_token)) {
                        Buwd_Options::update_job_option($job_id, 'gdrivetoken', $access_token);
                    } else {
                        Buwd_Options::delete_job_option($job_id, 'gdrivetoken');
                        set_site_transient('buwd_gdrive_error', __('GDrive: The refresh token was not received. Please try to authenticate again.', 'buwd'));
                    }

                    Buwd_Helper::redirect($redirect);
                    //wp_die();
                } catch (Exception $e) {
                    set_site_transient('buwd_gdrive_error', sprintf(__('GDrive API: %s', 'buwd'), $e->getMessage()));
                    Buwd_Options::delete_job_option($job_id, 'gdrivetoken');
                    Buwd_Helper::redirect($redirect);
                    //wp_die();
                }
            } else {
                try {
                    $access_token = Buwd_Options::get($job_id, 'gdrivetoken');
                    if (!empty($access_token)) {
                        $client->setAccessToken($access_token);
                        if ($client->isAccessTokenExpired()) {
                            $client->revokeToken($access_token);
                            Buwd_Options::delete_job_option($job_id, 'gdrivetoken');
                        }
                    }

                    $auth_url = $client->createAuthUrl();

                    wp_redirect($auth_url);
                    exit;
                } catch (Exception $e) {
                    set_site_transient('buwd_gdrive_error', sprintf(__('GDrive API: %s', 'buwd'), $e->getMessage()));
                    Buwd_Options::delete_job_option($job_id, 'gdrivetoken');
                    Buwd_Helper::redirect($redirect);
                    //	wp_die();
                }
            }
        }
    }

    public function get_footer()
    {
        $disabled = '';
        if (!$this->is_keys_setted()) {
            $disabled = 'buwd-disabled';
        }

        return '<button class="buwd-button button-save ' . $disabled . '" onclick="if(jQuery(this.form).valid()){this.form.submit()}"><span></span>Save</button>';
    }

    public function run(Buwd_Job $job_object)
    {
        $job_object->buwd_logger->set_log(sprintf(__('%d. Attempted to upload backup file to Google Drive.', 'buwd'), $job_object->steps_data[$job_object->current_step]['step']));
        $job_object->update_progress();

        try {
            $client = $this->GDrive();
            $gdrivetoken = $job_object->job['gdrivetoken'];
            $client->refreshToken($gdrivetoken['refresh_token']);

            $this->service = new Google_Service_Drive($client);
            if (empty($job_object->steps_data[$job_object->current_step]['folder_id'])) {
                $job_object->steps_data[$job_object->current_step]['folder_id'] = $this->get_folder_id($job_object->job['gdrivefolder']);
            }

            $access_token = $client->getAccessToken();
            if (empty($job_object->steps_data[$job_object->current_step]['session_uri'])) {
                $post_data = new stdClass();
                $post_data->title = $job_object->backup_file;
                $post_data->mimeType = Buwd_File::mime_content_type($job_object->backup_folder . $job_object->backup_file);

                if ($job_object->steps_data[$job_object->current_step]['folder_id'] !== 'root') {
                    $post_data_parent = new stdClass();
                    $post_data_parent->kind = 'drive#fileLink';
                    $post_data_parent->id = $job_object->steps_data[$job_object->current_step]['folder_id'];
                    $post_data->parents = array($post_data_parent);
                }


                $data = json_encode($post_data);

                $http_client = new WP_Http();

                $url = 'https://www.googleapis.com/upload/drive/v2/files?uploadType=resumable';

                $headers = array(
                    'Authorization'           => 'Bearer ' . $access_token['access_token'],
                    'Content-Length'          => strlen($data),
                    'X-Upload-Content-Type'   => Buwd_File::mime_content_type(
                        $job_object->backup_folder . $job_object->backup_file
                    ),
                    'Content-Type'            => 'application/json; charset=UTF-8',
                    'X-Upload-Content-Length' => $job_object->backup_file_size,
                );

                $response = $http_client->request($url, array(
                    'method'  => 'POST',
                    'body'    => json_encode($post_data),
                    'headers' => $headers
                ));

                if ($response['response']['code'] == 200 || $response['response']['code'] == 201) {
                    if (isset($response['headers']['location'])) {
                        $job_object->steps_data[$job_object->current_step]['session_uri'] = trim($response['headers']['location']);
                    }
                }

                if (empty($job_object->steps_data[$job_object->current_step]['session_uri'])) {
                    $job_object->buwd_logger->set_log(__('Resumable file transfer to Google Drive could not be established. Chunk transfer to Google Drive failed.', 'buwd'), E_USER_ERROR);
                    $job_object->update_progress();

                    return false;
                }
            }

            $job_object->buwd_logger->set_log(__('Upload to Google Drive has started. Please note, that this may take a while.', 'buwd'));
            $job_object->update_progress();

            $chunk_size = 5 * 1024 * 1024;

            if (!$job_object->steps_data[$job_object->current_step]['sub_step']) {
                $job_object->steps_data[$job_object->current_step]['sub_step'] = 0;
            }

            if ($handle = fopen($job_object->backup_folder . $job_object->backup_file, 'rb')) {
                fseek($handle, $job_object->steps_data[$job_object->current_step]['sub_step']);
                while (!feof($handle)) {

                    $job_object->restart_if_needed();
                    $job_object->check_if_stopped();
                    $chunk_data = fread($handle, $chunk_size);

                    $response = $http_client->request($job_object->steps_data[$job_object->current_step]['session_uri'], array(
                        'method'  => 'PUT',
                        'body'    => $chunk_data,
                        'headers' => array(
                            'Authorization'  => 'Bearer ' . $access_token['access_token'],
                            'Content-Length' => strlen($chunk_data),
                            'Content-Type'   => ' application/json; charset=UTF-8',
                            'Content-Range'  => 'bytes ' . $job_object->steps_data[$job_object->current_step]['sub_step'] . '-' . ($job_object->steps_data[$job_object->current_step]['sub_step'] + strlen($chunk_data) - 1) . '/' . $job_object->backup_file_size,

                        ),
                        'timeout' => 1500
                    ));

                    update_site_option('response_gdrive',$response);
                    if ($response['response']['code'] == 200 || $response['response']['code'] == 201 || $response['response']['code'] == 308) {
                        //$json_data = mb_substr($response['body'], $curl_header_size);
                        // $uploaded_file = json_decode($json_data);
                        $uploaded_file = json_decode($response['body']);
                        if ($response['response']['code'] == 308) {
                            $job_object->steps_data[$job_object->current_step]['sub_step'] = $job_object->steps_data[$job_object->current_step]['sub_step'] + $chunk_size;
                        }
                    } else {
                        $job_object->buwd_logger->set_log(__('An error occurred while trying to transfer file chunks to Google Drive.', 'buwd'), E_USER_WARNING);
                        $job_object->update_progress();

                        return false;
                    }
                }
                fclose($handle);

            } else {
                $job_object->buwd_logger->set_log(__('Could not open source file for transfer.', 'buwd'), E_USER_ERROR);
                $job_object->update_progress();

                return false;
            }

            if (is_object($uploaded_file) && isset($uploaded_file->id)) {
                if ($uploaded_file->fileSize == $job_object->backup_file_size) {
                    $downloadUrl = str_replace('&gd=true', '', $uploaded_file->downloadUrl);
                    $last_file = array();
                    $UTC = new DateTimeZone("UTC");
                    $cd = new DateTime($uploaded_file->createdDate);
                    $last_file['file'] = $uploaded_file->title;
                    $last_file['folder'] = $job_object->job['gdrivefolder'];
                    $last_file['time'] = (int)$cd->setTimezone($UTC)->getTimestamp();
                    $last_file['size'] = $uploaded_file->fileSize;
                    $last_file['downloadurl'] = $downloadUrl;
                    $last_file['file_id'] = $uploaded_file->id;
                    $last_file['jid'] = $job_object->job_id;
                    $last_file['dest'] = 'gdrive';
                    $last_file['logfile'] = basename($job_object->buwd_logger->logfile);

                    $dest_files = get_site_option('buwd-dest-gdrive-' . $job_object->job_id, array());
                    $dest_files[] = $last_file;

                    update_site_option('buwd-dest-gdrive-' . $job_object->job_id, $dest_files);

                    $job_object->buwd_logger->set_log(sprintf(__('Backup was transferred to %s', 'buwd'), $uploaded_file->alternateLink));
                    $job_object->update_progress();
                } else {
                    $job_object->buwd_logger->set_log(__('Backup file size and uploaded file size do not match.', 'buwd'), E_USER_ERROR);
                    $job_object->update_progress();

                    return false;
                }
            } else {
                $job_object->buwd_logger->set_log(__('An error occurred while trying to transfer backup to Google Drive.', 'buwd'), E_USER_ERROR);
                $job_object->update_progress();

                return false;
            }

            //delete files
            $backup_files = array();
            $files_list = $this->service->files->listFiles(array('fields' => 'files', 'q' => "explicitlyTrashed = false and '" . $job_object->steps_data[$job_object->current_step]['folder_id'] . "' in parents and mimeType = '" . Buwd_File::mime_content_type($job_object->backup_folder . $job_object->backup_file) . "'"));

            $deleted = 0;
            $files_to_delete = array();
            if ($files_list && $files_list->files) {
                foreach ($files_list->files as $file) {
                    $backup_files[strtotime($file->createdTime)] = $file->id;
                }

                if ((int)$job_object->job['gdrivefiledelete'] > 0 && count($backup_files) > (int)$job_object->job['gdrivefiledelete']) {
                    ksort($backup_files);
                    while (count($backup_files) > $job_object->job['gdrivefiledelete']) {
                        $file = array_shift($backup_files);
                        $this->service->files->delete($file);

                        $deleted++;
                        $files_to_delete[] = $file;
                    }
                }
            }

            if (!empty($files_to_delete)) {
                Buwd_Options::backup_bulk_delete('buwd-dest-gdrive-' . $job_object->job_id, $files_to_delete);
            }

            if ($deleted > 0) {
                $job_object->buwd_logger->set_log(sprintf(__('%d files were successfully deleted from Google Drive.', 'buwd'), $deleted));
                $job_object->update_progress();
            }
        } catch (Exception $e) {
            $job_object->buwd_logger->set_log(sprintf(__('Google Drive API: %s', 'buwd'), $e->getMessage()), $e->getFile(), $e->getLine(), E_USER_ERROR);
            $job_object->update_progress();

            return false;
        }

        return true;
    }

    public function run_sync(Buwd_Job $job_object)
    {
        $job_object->buwd_logger->set_log(sprintf(__('%d. Attempted to sync files to Google Drive.', 'buwd'), $job_object->steps_data[$job_object->current_step]['step']));
        $job_object->update_progress();

        try {
            $client = $this->GDrive();
            $gdrivetoken = $job_object->job['gdrivetoken'];
            $client->refreshToken($gdrivetoken['refresh_token']);

            $this->service = new Google_Service_Drive($client);

            $sync_folder_id = $this->get_folder_id($job_object->job['gdrivefolder']);
            $extra_files = array();
            if ($job_object->extra_files) {
                foreach ($job_object->extra_files as $extra_file) {
                    $extra_files[basename($extra_file)] = $extra_file;
                }
            }

            if (!isset($job_object->steps_data[$job_object->current_step]['folder_key'])) {
                $job_object->steps_data[$job_object->current_step]['folder_key'] = -1;
                $job_object->steps_data[$job_object->current_step]['folder_size'] = 0;
            }


            $files_folders = array();
            $files_folders_dir = Buwd::get_plugin_data('temp_folder_dir') . 'job_files_folders.php';
            if (file_exists($files_folders_dir)) {
                $files_folders = unserialize(file_get_contents($files_folders_dir));
            }
            if (!isset($files_folders['files'])) {
                $files_folders['files'] = array();
            }

            $job_object->buwd_logger->set_log(__('Modified files were synced to Google Drive.', 'buwd'));
            $job_object->update_progress();


            //$drive_folder_id = $this->get_folder_id($sync_folder_id);
            $parameters = array('fields' => 'files', 'q' => "explicitlyTrashed = false and '" . $sync_folder_id . "' in parents");
            $pageToken = null;
            $drive_files = array();
            do {
                try {
                    if ($pageToken) {
                        $parameters['pageToken'] = $pageToken;
                    }
                    $files = $this->service->files->listFiles($parameters);
                    $drive_files[] = $files->getFiles();

                    $pageToken = $files->getNextPageToken();
                } catch (Exception $e) {
                    $pageToken = null;
                }
            } while ($pageToken);


            foreach ($files_folders['folders'] as $folder_key => $folder) {
                if ($job_object->steps_data[$job_object->current_step]['folder_key'] >= $folder_key) {
                    continue;
                }

                if (!isset($job_object->steps_data[$job_object->current_step][$folder_key])) {
                    $job_object->steps_data[$job_object->current_step][$folder_key] = array();
                    $job_object->steps_data[$job_object->current_step][$folder_key]['file_key'] = -1;
                }

                $job_object->check_if_stopped();
                $job_object->restart_if_needed();
                $folder_dirname = $job_object->job['gdrivefolder'] . '/' . substr($folder, strlen($job_object->job['abs_path']));

                $drive_folder_id = $this->get_folder_id($folder_dirname);
                $parameters = array('fields' => 'files', 'q' => "explicitlyTrashed = false and '" . $drive_folder_id . "' in parents and mimeType != 'application/vnd.google-apps.folder'");
                $pageToken = null;
                $drive_files = array();
                do {
                    try {
                        if ($pageToken) {
                            $parameters['pageToken'] = $pageToken;
                        }
                        $files = $this->service->files->listFiles($parameters);

                        $drive_files[] = $files->getFiles();
                        $pageToken = $files->getNextPageToken();
                    } catch (Exception $e) {
                        $pageToken = null;
                    }
                } while ($pageToken);


                if ($sync_folder_id === $drive_folder_id && $drive_files) {
                    foreach ($drive_files as $drive_key => $drive_file) {
                        if (in_array((string)$drive_file->name, array_keys($extra_files))) {
                            unset($drive_files[$drive_key]);
                        }
                    }
                }

                $folder_files = isset($files_folders['files'][$folder]) ? $files_folders['files'][$folder] : array();
                foreach ($folder_files as $file_key => $f_file) {
                    if ($job_object->steps_data[$job_object->current_step][$folder_key]['file_key'] >= $file_key) {
                        continue;
                    }

                    $job_object->check_if_stopped();
                    $job_object->restart_if_needed();
                    //TODO in future save file key for restart
                    foreach ($drive_files as $drive_key => $drive_file) {
                        if ($drive_file->name == basename($f_file)) {
                            if ($drive_file->md5Checksum != md5_file($f_file)) {

                                $updated_file = new Google_Service_Drive_DriveFile();

                                $this->service->files->update($drive_file->id, $updated_file, array(
                                    'data'       => file_get_contents($f_file),
                                    'uploadType' => 'multipart',
                                    'mimeType'   => Buwd_File::mime_content_type($f_file)
                                ));
                                $job_object->steps_data[$job_object->current_step]['folder_size'] += filesize($f_file);

                                $job_object->buwd_logger->set_log(sprintf(__('%s file was updated on Google Drive.', 'buwd'), $f_file));
                                $job_object->update_progress();
                            }
                            //remove existing file from gdrive files array
                            unset($drive_files[$drive_key]);
                            continue 2;
                        }
                    }

                    $new_file = new Google_Service_Drive_DriveFile();
                    $new_file->setName(basename($f_file));
                    $new_file->setMimeType(Buwd_File::mime_content_type($f_file));
                    //$parent = new Google_Service_Drive_ParentReference();
                    //$parent->setId($drive_folder_id);
                    $new_file->setParents(array($drive_folder_id));
                    $this->service->files->create($new_file, array(
                        'data'       => file_get_contents($f_file),
                        'uploadType' => 'multipart',
                        'mimeType'   => Buwd_File::mime_content_type($f_file)
                    ));
                    $job_object->steps_data[$job_object->current_step]['folder_size'] += filesize($f_file);

                    $file_dirname = $job_object->job['gdrivefolder'] . '/' . substr($f_file, strlen($job_object->job['abs_path']));

                    $job_object->buwd_logger->set_log(sprintf(__('%s file was uploaded to Google Drive.', 'buwd'), $file_dirname));
                    $job_object->update_progress();

                    $job_object->steps_data[$job_object->current_step][$folder_key]['file_key'] = $file_key;
                }

                if (!$job_object->job['gdrivefiledeletesync'] && !empty($drive_files)) {
                    foreach ($drive_files as $drive_key => $drive_file) {
                        $job_object->restart_if_needed();

                        $this->service->files->delete($drive_file->id);

                        $job_object->buwd_logger->set_log(sprintf(__('%s file was deleted from Google Drive.', 'buwd'), $file_dirname . $drive_file->name));
                        $job_object->update_progress();
                    }
                }

                $job_object->steps_data[$job_object->current_step]['folder_key'] = $folder_key;
            }

            //add extra files
            if ($extra_files) {
                $drive_files = $this->service->files->listFiles(array('fields' => 'files', 'q' => "explicitlyTrashed = false and '" . $sync_folder_id . "' in parents and mimeType != 'application/vnd.google-apps.folder'"));
                $job_object->check_if_stopped();
                foreach ($extra_files as $extra_file_basename => $extra_file) {
                    $job_object->restart_if_needed();
                    foreach ($drive_files as $drive_file_key => $drive_file) {
                        if ($drive_file->name == $extra_file_basename) {
                            if ($drive_file->md5Checksum != md5_file($extra_file)) {
                                $this->service->files->delete($drive_file->id);

                                $new_file = new Google_Service_Drive_DriveFile();
                                $new_file->setName($extra_file_basename);
                                $new_file->setMimeType(Buwd_File::mime_content_type($extra_file));
                                $new_file->setParents(array($sync_folder_id));
                                $this->service->files->create($new_file, array(
                                    'data'       => file_get_contents($extra_file),
                                    'uploadType' => 'multipart',
                                    'mimeType'   => Buwd_File::mime_content_type($extra_file)
                                ));

                                /*  $update_extra_file = new Google_Service_Drive_DriveFile();
                                $this->service->files->update($drive_file->id, $update_extra_file, array(
                                     'data'       => file_get_contents($extra_file),
                                     'uploadType' => 'multipart',
                                     'mimeType'   => Buwd_File::mime_content_type($extra_file)
                                 ));*/

                                $job_object->steps_data[$job_object->current_step]['folder_size'] += filesize($extra_file);
                            }

                            $job_object->buwd_logger->set_log(sprintf(__('%s extra file was updated on Google Drive.', 'buwd'), $extra_file));
                            $job_object->update_progress();
                            continue 2;
                        }
                    }
                    $file = new Google_Service_Drive_DriveFile();
                    $file->setName($extra_file_basename);
                    $file->setMimeType(Buwd_File::mime_content_type($extra_file));
                    // $parent = new Google_Service_Drive_ParentReference();
                    // $parent->setId($sync_folder_id);
                    $file->setParents(array($sync_folder_id));
                    $res = $this->service->files->create($file, array(
                        'data'       => file_get_contents($extra_file),
                        'uploadType' => 'multipart',
                        'mimeType'   => Buwd_File::mime_content_type($extra_file)
                    ));
                    $job_object->steps_data[$job_object->current_step]['folder_size'] += filesize($extra_file);
                    if ($res->size == filesize($extra_file)) {
                        $job_object->buwd_logger->set_log(sprintf(__('%s extra file was uploaded to Google Drive.', 'buwd'), $job_object->job['gdrivefolder'] . '/' . $extra_file_basename));
                        $job_object->update_progress();
                    }
                }
            }

            $sync_file = get_site_option('buwd-dest-gdrive-sync-' . $job_object->job_id, array());
            $sync_file['file'] = 'Synchronized';
            $sync_file['folder'] = $job_object->job['gdrivefolder'];
            $sync_file['time'] = current_time('timestamp', true);
            $sync_file['size'] = $job_object->steps_data[$job_object->current_step]['folder_size'];
            $sync_file['jid'] = $job_object->job_id;
            $sync_file['dest'] = 'gdrive';
            $sync_file['logfile'] = basename($job_object->buwd_logger->logfile);
            $sync_file['sync'] = 1;

            update_site_option('buwd-dest-gdrive-sync-' . $job_object->job_id, $sync_file);


        } catch (Exception $e) {
            $job_object->buwd_logger->set_log(sprintf(__('Google Drive API: %s', 'buwd'), $e->getMessage()), $e->getFile(), $e->getLine(), E_USER_ERROR);
            $job_object->update_progress();

            return false;
        }

        return true;
    }

    private function GDrive($config = array(), $redirectUri = null)
    {
        $client = new Google_Client($config);
        $client->setApplicationName(Buwd::get_plugin_data('name'));
        $client->setClientId($this->gd_client_id);
        $client->setClientSecret($this->gd_client_secret);
        $client->setScopes(array('https://www.googleapis.com/auth/drive'));
        if ($redirectUri) {
            $client->setRedirectUri($redirectUri);
        }
        $client->setAccessType('offline');

        return $client;
    }

    /**
     * Create folder if not exist
     *
     * @param $path folder path
     *              return folder id
     */
    private function get_folder_id($path)
    {
        $folder_id = 'root';
        if ($path != '/') {
            $folders = explode('/', trim($path, '/'));
            $current_path = '';

            foreach ($folders as $folder) {
                $folder_exist = false;
                $current_path .= '/' . $folder;

                $sub_folders = $this->service->files->listFiles(array('fields' => 'files', 'q' => "explicitlyTrashed = false and '" . $folder_id . "' in parents and mimeType = 'application/vnd.google-apps.folder'"));

                if ($sub_folders) {
                    foreach ($sub_folders as $sub_folder) {
                        if ($sub_folder->name == $folder) {
                            $folder_id = $sub_folder->id;
                            $folder_exist = true;
                        }
                    }

                }

                if (!$folder_exist) {
                    $folder_id = $this->create_folder($folder, $folder_id);
                }
            }
        }

        return $folder_id;
    }

    /**
     * Create new folder
     *
     * @param $folder_name
     * @param $parent_id
     *
     * @return folder id
     */
    private function create_folder($folder_name, $parent_id)
    {
        $folder = new Google_Service_Drive_DriveFile();
        $folder->setName($folder_name);
        $folder->setMimeType('application/vnd.google-apps.folder');

        if ($parent_id != 'root') {
            //            $parent = new Google_Service_Drive_ParentReference();
            //            $parent->setId($parent_id);
            $folder->setParents(array($parent_id));
        }

        try {
            $created_folder = $this->service->files->create($folder, array('mimeType' => 'application/vnd.google-apps.folder'));
        } catch (Exception $e) {

            die();
        }

        return $created_folder->id;
    }

    public function save_form($job_id, $field_names)
    {
        foreach ($field_names as $field_name) {
            $field_value = Buwd_Helper::get($field_name) ? Buwd_Helper::get($field_name) : '';
            if ($field_name == 'gdrivefolder') {
                $field_value = sanitize_text_field($field_value);
                $field_value = untrailingslashit(str_replace(array('//', '\\'), '/', trim($field_value)));
                if (substr($field_value, 0, 1) != '/') {
                    $field_value = '/' . ($field_value);
                }
            }

            if ($field_name == 'gdrivefiledelete') {
                $field_value = (int)
                $field_value;
            }

            Buwd_Options::update_job_option($job_id, $field_name, $field_value);
        }
    }

    public function delete_file($backup)
    {
        $file_id = $backup['file_id'];
        $job_id = $backup['jid'];

        try {
            $client = $this->GDrive();
            $gdrivetoken = Buwd_Options::get($job_id, 'gdrivetoken');
            $client->refreshToken($gdrivetoken['refresh_token']);

            $this->service = new Google_Service_Drive($client);
            $this->service->files->delete($file_id);

        } catch (Exception $e) {

            //set_site_transient( 'buwd_backups_error', $e->getMessage() );
            return false;
        }

        return true;
    }

    public function download_file($backup)
    {
        $file_id = $backup['file_id'];
        $job_id = $backup['jid'];
        $size = $backup['size'];

        try {
            $client = $this->GDrive();
            $gdrivetoken = Buwd_Options::get($job_id, 'gdrivetoken');
            $client->refreshToken($gdrivetoken['refresh_token']);

            $this->service = new Google_Service_Drive($client);
            $response = $this->service->files->get($file_id, array("alt" => "media"));

            @set_time_limit(3000);
            nocache_headers();
            while (ob_get_level() > 0) {
                @ob_end_clean();
            }
            header('Content-Description: File Transfer');
            header('Content-Type: ' . Buwd_File::mime_content_type($backup['file']));
            header('Content-Disposition: attachment; filename="' . basename($backup['file']) . '"');
            header('Content-Transfer-Encoding: binary');
            header('Content-Length: ' . $size);
            echo $response->getBody()->getContents();
            die();

        } catch (Exception $e) {
            set_site_transient('buwd_backups_error', $e->getMessage());
            Buwd_Helper::redirect(array('page' => 'buwd_backups'));
        }

    }

    public static function display_messages()
    {
        if ($error = get_site_transient('buwd_gdrive_error')) {
            echo Buwd_Helper::message($error, 'error');
            delete_site_transient('buwd_gdrive_error');
        } else if ($updated = get_site_transient('buwd_gdrive_updated')) {
            echo Buwd_Helper::message($updated, 'success');
            delete_site_transient('buwd_gdrive_updated');
        }
    }

    public function is_valid($job_options)
    {
        if (empty($job_options['gdrivetoken'])) {
            return false;
        }

        return true;
    }

    /**
     * Return an instance of this class.
     */
    public static function get_instance()
    {
        if (null == self::$instance) {
            self::$instance = new self;
        }

        return self::$instance;
    }
}

?>