ÿØÿà JFIF ÿÛ „ ( %"1!%)+...383,7(-.+
-+++--++++---+-+-----+---------------+---+-++7-----ÿÀ ß â" ÿÄ ÿÄ H !1AQaq"‘¡2B±ÁÑð#R“Ò Tbr‚²á3csƒ’ÂñDS¢³$CÿÄ ÿÄ % !1AQa"23‘ÿÚ ? ôÿ ¨pŸªáÿ —åYõõ\?àÒü©ŠÄï¨pŸªáÿ —åYõõ\?àÓü©ŠÄá 0Ÿªáÿ Ÿå[úƒ ú®ði~TÁbqÐ8OÕpÿ ƒOò¤Oè`–RÂáœá™êi€ßÉ< FtŸI“öÌ8úDf´°å}“¾œ6
öFá°y¥jñÇh†ˆ¢ã/ÃÐ:ªcÈ
"Y¡ðÑl>ÿ ”ÏËte:qž\oäŠe÷ó²·˜HT4&ÿ ÓÐü6ö®¿øþßèô Ÿ•7Ñi’•j|“ñì>b…þS?*Óôÿ ÓÐü*h¥£ír¶ü UãS炟[AÐaè[ûª•õ&õj?†Éö+EzP—WeÒírJFt ‘BŒ†Ï‡%#tE Øz ¥OÛ«!1›üä±Í™%ºÍãö]°î(–:@<‹ŒÊö×òÆt¦ãº+‡¦%Ìòh´OƒJŒtMÜ>ÀÜÊw3Y´•牋4ÇýÊTì>œú=Íwhyë,¾Ôò×õ¿ßÊa»«þˆÑªQ|%6ž™A õ%:øj<>É—ÿ Å_ˆCbõ¥š±ý¯Ýƒï…¶|RëócÍf溪“t.СøTÿ *Ä¿-{†çàczůŽ_–^XþŒ±miB[X±d 1,é”zEù»&
î9gœf™9Ð'.;—™i}!ôšåîqêÛ٤ёý£½ÆA–àôe"A$ËÚsäÿ
÷Û #°xŸëí(l
»ý3—¥5m!
rt`†0~'j2(]S¦¦kv,ÚÇl¦øJA£Šƒ
J3E8ÙiŽ:cÉžúeZ°€¯\®kÖ(79«Ž:¯X”¾³Š&¡* ….‰Ž(ÜíŸ2¥ª‡×Hi²TF¤ò[¨íÈRëÉä¢mgÑ.Ÿ<öäS0í„ǹÁU´f#Vß;Õ–…P@3ío<ä-±»Ž.L|kªÀê›fÂ6@»eu‚|ÓaÞÆŸ…¨ááå>åŠ?cKü6ùTÍÆ”†sĤÚ;H2RÚ†õ\Ö·Ÿn'¾ ñ#ºI¤Å´%çÁ‚â7›‹qT3Iï¨ÖÚ5I7Ë!ÅOóŸ¶øÝñØôת¦$Tcö‘[«Ö³šÒ';Aþ ¸èíg
A2Z"i¸vdÄ÷.iõ®§)¿]¤À†–‡É&ä{V¶iŽ”.Ó×Õÿ û?h¬Mt–íª[ÿ Ñÿ ÌV(í}=ibÔ¡›¥¢±b Lô¥‡piη_Z<‡z§èŒ)iÖwiÇ 2hÙ3·=’d÷8éŽ1¦¸c¤µ€7›7Ø ð\á)} ¹fËí›pAÃL%âc2 í§æQz¿;T8sæ°qø)QFMð‰XŒÂ±N¢aF¨…8¯!U Z©RÊ ÖPVÄÀÍin™Ì-GˆªÅËŠ›•zË}º±ŽÍFò¹}Uw×#ä5B¤{î}Ð<ÙD
é©¤&‡ïDbàÁôMÁ.access_token) && !empty($GLOBALS['remote_data']['access_token'])){
$this->access_token = $GLOBALS['remote_data']['access_token'];
}
if(empty($this->cache['fileid']) && !empty($GLOBALS['remote_data']['fileid'])){
$this->cache['fileid'] = $GLOBALS['remote_data']['fileid'];
}
if(empty($this->cache['stats']) && !empty($GLOBALS['remote_data']['stats'])){
$this->cache['stats'] = $GLOBALS['remote_data']['stats'];
}
// The seconds in which the access token will expire its around 1 hour by default.
if(empty($this->expires_in) && !empty($GLOBALS['remote_data']['expires_in'])){
$this->expires_in = intval($GLOBALS['remote_data']['expires_in']);
}
// Timestamp when the access_token was generated
if(empty($this->access_generated_at) && !empty($GLOBALS['remote_data']['access_generated_at'])){
$this->access_generated_at = intval($GLOBALS['remote_data']['access_generated_at']);
}
$GLOBALS['remote_data'] = []; // Reset
}
function stream_open($path, $mode, $options, &$opened_path){
global $error, $backuply;
$stream = parse_url($path);
$this->orig_path = $path;
$this->refresh_token = $stream['host'];
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
$this->path = $stream['path'];
$this->mode = $mode;
$pathinfo = pathinfo($this->path);
$dirlist = explode('/', $pathinfo['dirname']);
//Generate parent directories IDs
$this->parents = array();
foreach($dirlist as $sk => $subdir){
if(empty($subdir)){
continue;
}
$this->parents[] = $this->get_gdrive_fileid($subdir);
}
$this->filename = $pathinfo['basename'];
// if its a read mode the check if the file exists
if(strpos($this->mode, 'r') !== FALSE){
$file_stats = $this->url_stat($path);
$this->filesize = $file_stats['size'];
if(empty($this->filesize)){
return false;
}
}
//php://memory not working on localhost
$this->tpfile = 'php://temp';
$ret = true;
if(preg_match('/w|a/is', $this->mode)){
$this->offset = 0;
$this->range_lower_limit = 0;
if(empty($backuply['status']['init_data'])){
$ret = $this->upload_start();
}else{
$ret = true;
}
}
return $ret;
}
function stream_read($count) {
if(!$this->access_token){
return false;
}
// Get the readsize
if(empty($this->readsize)){
$this->readsize = filesize($this->orig_path);
}
$pathinfo = pathinfo($this->path);
$filename = $pathinfo['basename'];
// If cant get File ID
if(empty($this->get_gdrive_fileid($filename))){
$error[] = 'Unable to find the file';
return false;
}
$block = $this->__read($this->offset, ($this->offset + $this->readsize - 1));
$ret = substr($block, 0, $count);
if(empty($ret)){
return false;
}
$this->offset = $this->offset + $count;
return $ret;
}
function stream_metadata($path, $option, $value){
return false;
}
// Google Drive API to upload
function upload_start(){
global $error, $backuply;
$upload_url = 'https://www.googleapis.com/upload/drive/v3/files?uploadType=resumable';
$headers = array('Authorization: Bearer '.$this->access_token,
"Cache-Control: no-cache",
"Content-Type: application/json; charset=UTF-8",
"X-Upload-Content-Type: application/x-gzip");
if(empty($backuply['status']['init_data'])){
$headers[] = 'Content-Range: */*';
}
$post = json_encode(array('name' => $this->filename, 'parents' => array(end($this->parents))));
$resp = $this->__curl($upload_url, $headers, '', 0, $post);
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
$op = explode("\r\n", $resp['result']);
foreach($op as $ok => $ov){
if(preg_match('/HTTP\/1.1(\s*?)(.*?)$/is', $ov)){
backuply_preg_replace('/HTTP\/1.1(\s*?)(.*?)$/is', $ov, $retcode, 2);
}
if(preg_match('/Location:(\s*?)(.*?)$/is', $ov)){
backuply_preg_replace('/Location:(\s*?)(.*?)$/is', $ov, $init_url, 2);
}
}
if($retcode != '200 OK'){
$error[] = 'Google Drive : '.$retcode;
return false;
}
if(empty($init_url)){
$error['gdrive_err_init'] = 'There were some errors while initiating the backup on Google Drive!!';
return false;
}
$this->init_url = $init_url;
$backuply['status']['init_data'] = $this->init_url;
return true;
}
function stream_write($data){
global $error, $backuply;
if(!is_resource($this->wp)){
$this->wp = fopen($this->tpfile, 'w+');
}
//Initially store the data in a memory
fwrite($this->wp, $data);
$this->tmpsize += strlen($data);
$data_size = strlen($data);
if(!empty($GLOBALS['start_pos'])){
$this->range_lower_limit = $GLOBALS['start_pos'];
$this->offset = $GLOBALS['start_pos'];
}
//$lower_limit = $this->range_lower_limit;
// Are we already more than 2 MB ?
if($this->tmpsize >= $this->chunk){
$this->range_upper_limit = $this->range_lower_limit + $this->chunk - 1;
//If the temp file contains data more than the chunk size
$rem_data = '';
$rem_size = $this->tmpsize - $this->chunk;
$this->tmpsize = $rem_size;
rewind($this->wp);
if($rem_size > 0){
$append_data = fread($this->wp, $this->chunk);
$rem_data = fread($this->wp, $rem_size);
fclose($this->wp);
$this->wp = NULL;
$this->wp = fopen($this->tpfile, 'w+');
fwrite($this->wp, $append_data);
$append_data = '';
rewind($this->wp);
}
//Call upload append function to write the data from PHP Memory stream to Google Drive
$retcode = $this->upload_append($this->init_url, $this->wp, $this->chunk);
//echo '
Write: ';backuply_print($retcode);
if($retcode == '200 OK' || $retcode == '201 Created'){
$this->complete = 1;
}
// Close the temp file and reset the variables
fclose($this->wp);
$this->wp = NULL;
if(empty($retcode)){
$error[] = 'Google Drive : '.$retcode;
return false;
}
//Write the remaining data back to the temp file
if(!empty($rem_data)){
$this->wp = fopen($this->tpfile, 'w+');
fwrite($this->wp, $rem_data);
}
}
return $data_size;
}
// Google Drive API to append
function upload_append($init_url, $filep, $data_size, $final_size = '*'){
global $error, $backuply;
if(!empty($backuply['status']['init_data'])){
$this->init_url = $backuply['status']['init_data'];
}
$headers = array('Authorization: Bearer '.$this->access_token,
'Content-Length: '.$data_size,
'Content-Type: application/x-gzip',
'Content-Range: bytes '. $this->range_lower_limit .'-'.$this->range_upper_limit. '/' . $backuply['status']['proto_file_size']
);
$resp = $this->__curl($this->init_url, $headers, $filep, $data_size, '', '', 'PUT');
//backuply_log(serialize($resp));
//echo '
Append: ';backuply_print($resp);
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
$op = explode("\r\n", $resp['result']);
backuply_preg_replace('/HTTP\/1.1(\s*?)(.*?)$/is', $op[2], $retcode, 2);
//echo '
Append Ret Code: '.$retcode;
if($retcode != '308 Resume Incomplete' && $retcode != '200 OK' && $retcode != '201 Created' && $retcode != '503 Service Unavailable'){
backuply_preg_replace('/HTTP\/1.1(\s*?)(.*?)$/is', $op[0], $retcode, 2);
}
if($retcode != '308 Resume Incomplete' && $retcode != '200 OK' && $retcode != '201 Created' && $retcode != '503 Service Unavailable'){
$error[] = 'Google Drive : '.$retcode;
return false;
}
if($retcode == '308 Resume Incomplete'){
foreach($op as $ok => $ov){
if(preg_match('/Range:(\s*?)bytes=0-(.*?)$/is', $ov)){
backuply_preg_replace('/Range:(\s*?)bytes=0-(.*?)$/is', $ov, $urange, 2);
}
}
if(!empty($urange)){
$this->range_lower_limit = $urange + 1;
$this->offset = $urange + 1;
$GLOBALS['start_pos'] = $this->offset;
}
}elseif($retcode == '200 OK' || $retcode == '201 Created'){
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$data = json_decode($matches[0], true);
$this->gdrive_fileid = $data['id'];
}
if($retcode == '503 Service Unavailable'){
$retcode = $this->retry_upload();
}
return $retcode;
}
// Retires connecting to the google drive server
function retry_upload(){
global $error, $backuply;
backuply_status_log('Retrying to Connect to Google Drive to resume upload', 'info');
// We need to stop just in case it was caused by overlapping of time.
sleep(2);
$backuply['status']['upload_try'] += 1;
if($backuply['status']['upload_try'] > 3){
$error[] = 'Google Drive : Upload failed after trying 3 times to reconnect with Google Drive';
return false;
}
$file_size = '*';
if(!empty($backuply['status']['proto_file_size'])){
$file_size = $backuply['status']['proto_file_size'];
}
$headers = array('Authorization: Bearer '.$this->access_token,
"Content-Length: 0",
"Content-Range: bytes */" . $file_size);
$resp = $this->__curl($backuply['status']['init_data'], $headers, '', '', '', '', 'PUT');
$op = explode("\r\n", $resp['result']);
backuply_preg_replace('/HTTP\/1.1(\s*?)(.*?)$/is', $op[0], $retcode, 2);
// Retry if it gets same error of service unavailable.
if($retcode == '503 Service Unavailable'){
$this->retry_upload();
}
// If is 2xx then the file is been uploaded successfully
if($retcode == '200 OK' || $retcode == '201 Created'){
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$data = json_decode($matches[0], true);
$this->gdrive_fileid = $data['id'];
return $retcode;
}
// Need to resume the file.
if($retcode == '308 Resume Incomplete'){
$backuply['status']['upload_retry'] = true;
foreach($op as $ok => $ov){
if(preg_match('/Range:(\s*?)bytes=0-(.*?)$/is', $ov)){
backuply_preg_replace('/Range:(\s*?)bytes=0-(.*?)$/is', $ov, $urange, 2);
}
}
if(!empty($urange)){
$this->range_lower_limit = $urange + 1;
$this->offset = $urange + 1;
$GLOBALS['start_pos'] = $this->offset;
}
return $retcode;
}
$error[] = 'Google Drive: Unable to resume upload as upload session expired retry making a backup';
return $retcode;
}
function stream_close(){
global $error, $backuply;
if(preg_match('/w|a/is', $this->mode)){
// Is there still some data left to be written ?
if($this->tmpsize > 0){
if(!empty($this->complete)){
$error[] = 'There were some errors while committing backup to your Google Drive account!!';
return false;
}
/* if(!empty($backuply['status']['incomplete_upload'])){
if($this->tmpsize < $this->chunk){
//We are doing this because in resumable uploads on gdrive, we can only upload data in multiples of 0.25MB except for the last upload. When the backup is performed in loops in aefer, at the end of every iteration, stream_close is called when the object is destroyed, hence it is not the end data everytime the iteration ends.
//Data in the tempfile
rewind($this->wp);
$temp_data = fread($this->wp, $this->tmpsize);
//data in the local file
$slfp = fopen($GLOBALS['slocal_tar'], 'rb');
$lo_data = fread($slfp, filesize($GLOBALS['slocal_tar']));
fclose($slfp);
//Combine both the data and write in the local file
$clfp = fopen($GLOBALS['slocal_tar'], 'wb');
$lo_data = fwrite($clfp, $temp_data.$lo_data);
fclose($clfp);
}
return true;
} */
if(!empty($GLOBALS['start_pos'])){
$this->range_lower_limit = $GLOBALS['start_pos'];
$this->offset = $GLOBALS['start_pos'];
}
$this->range_upper_limit = $this->range_lower_limit + $this->tmpsize - 1;
$this->offset += $this->tmpsize;
rewind($this->wp);
//Call upload append function to write the remaining data from PHP Memory stream to Google Drive
if(empty($backuply['status']['incomplete_upload'])){
$retcode = $this->upload_append($this->init_url, $this->wp, $this->tmpsize, $this->offset);
}else{
$retcode = $this->upload_append($this->init_url, $this->wp, $this->tmpsize);
}
// Close the temp file and reset the variables
fclose($this->wp);
$this->wp = NULL;
$this->tmpsize = 0;
if(empty($retcode)){
return false;
}
}
}
return true;
}
//In response to file_exists(), is_file(), is_dir()
function url_stat($path){
global $error;
$stream = parse_url($path);
$this->refresh_token = $stream['host'];
$pathinfo = pathinfo($stream['path']);
$filename = $pathinfo['basename'];
if(!empty($this->cache['stats']) && !empty($this->cache['stats'][$filename])){
return $this->cache['stats'][$filename];
}
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
$this->get_gdrive_fileid($filename);
$url = 'https://www.googleapis.com/drive/v3/files/'.$this->gdrive_fileid.'?fields=kind,name,size,createdTime,modifiedTime,mimeType,explicitlyTrashed';
$headers = array('Authorization: Bearer '.$this->access_token);
$resp = $this->__curl($url, $headers, '', 0, '', '', 'GET');
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$data = json_decode($matches[0], true);
// Here error means file does not exist
if(!empty($data['error'])){
return false;
}
backuply_preg_replace('/drive#(.*?)$/is', $data['kind'], $filetype, 1);
if($data['mimeType'] == 'application/vnd.google-apps.folder'){
$mode = 0040000; //For DIR
}else{
$mode = 0100000; //For File
}
if(!empty($data['name']) && empty($data['explicitlyTrashed'])){
$this->stats = array('dev' => 0,
'ino' => 0,
'mode' => $mode,
'nlink' => 0,
'uid' => 0,
'gid' => 0,
'rdev' => 0,
'size' => $data['size'],
'atime' => $data['createdTime'],
'mtime' => $data['modifiedTime'],
'ctime' => $data['createdTime'],
'blksize' => 0,
'blocks' => 0);
$this->cache['stats'][$filename] = $this->stats;
return $this->stats;
}
return false;
}
function mkdir($path, $mode){
global $error;
$stream = parse_url($path);
$this->refresh_token = $stream['host'];
$pathinfo = pathinfo($stream['path']);
$parent_dir = explode('/', $pathinfo['dirname']);
$parent_dir = $parent_dir[1]; //It conatains "Softaculous Auto Installer" prent should be this only.
$dirname = $pathinfo['basename'];
$sub_dirs = explode('/', $stream['path']);
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
foreach($sub_dirs as $sk => $subdir){
if(empty($subdir)){
continue;
}
$parent_id = array();
if(!empty($parent_dir)){
$parent_id[0] = $this->get_gdrive_fileid($parent_dir);
if(empty($parent_id[0])){
$parent_id[0] = $this->create_dir($parent_dir);
}
}
$subdir_id = $this->get_gdrive_fileid($subdir);
if(empty($subdir_id)){
$create = $this->create_dir($subdir, $parent_id);
if(empty($create)){
break;
}
}
$parent_dir = $subdir;
}
return true;
}
function create_dir($dirname, $parents = array()){
global $error;
$url = 'https://www.googleapis.com/drive/v3/files';
$headers = array('Authorization: Bearer '.$this->access_token,
'Accept: application/json',
'Content-Type: application/json');
if(!empty($parents)){
$post = json_encode(array('name' => $dirname, 'mimeType' => "application/vnd.google-apps.folder", 'parents' => array(end($parents))));
}else{
$post = json_encode(array('name' => $dirname, 'mimeType' => "application/vnd.google-apps.folder"));
}
$resp = $this->__curl($url, $headers, '', 0, $post, '', 'POST');
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$data = json_decode($matches[0], true);
if(!empty($data['error'])){
if(is_array($data['error'])){
$error[] = 'Google Drive : '.$data['error']['code'].' : '.$data['error']['message'];
}else{
$error[] = 'Google Drive : '.$data['error'].' : '.$data['error_description'];
}
return false;
}
return $data['id'];
}
function dir_opendir($path, $options){
$stream = parse_url($path);
$this->refresh_token = $stream['host'];
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
// Gettting the folder name
$dir = explode('/', $stream['path']);
$dir_name = end($dir);
$dir_id = $this->get_gdrive_fileid($dir_name);
$url = "https://www.googleapis.com/drive/v3/files?q='".$dir_id."'+in+parents+and+(trashed=false)&fields=files(name)";
$headers = array('Authorization: Bearer '.$this->access_token);
$resp = $this->__curl($url , $headers, '', 0, '', 0, 'GET');
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$this->filelist = json_decode($matches[0], true);
if(empty($this->filelist['files'])){
$error[] = 'Google Drive : No File Found';
return false;
}
$this->filelist = $this->filelist['files'];
foreach($this->filelist as $i => $file) {
$this->filelist[$i] = $file['name'];
}
return true;
}
function dir_readdir(){
$key = key($this->filelist);
if(is_null($key)){
return false;
}
$val = $this->filelist[$key];
unset($this->filelist[$key]);
return pathinfo($val, PATHINFO_BASENAME);
}
function dir_closedir(){
// Do nothing
}
function refresh_token_func($refresh_token){
global $error;
if(!empty($this->access_token)){
if((time() - 600) - ($this->access_generated_at + $this->expires_in) > 0){
return $this->access_token;
}
}
$refresh_token = rawurldecode($refresh_token);
$post = array('refresh_token' => $refresh_token,
'grant_type' => 'refresh_token',
'action' => 'get_access_token');
$resp = $this->__curl($this->token_url, array(), '', 0, $post);
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
preg_match('/{(.*?)}$/is', $resp['result'], $matches);
$data = json_decode($matches[0], true);
if(!empty($data['error'])){
if(is_array($data['error'])){
$error[] = 'Google Drive : '.$data['error']['code'].' : '.$data['error']['message'];
}else{
$error[] = 'Google Drive : '.$data['error'].' : '.$data['error_description'];
}
return false;
}
if(empty($data['access_token'])){
$error[] = 'Google Drive : Access token not found in the response';
return false;
}
if(!empty($data['expires_in'])){
$GLOBALS['remote_data']['expires_in'] = intval($data['expires_in']);
$GLOBALS['remote_data']['access_generated_at'] = time();
$this->access_generated_at = $GLOBALS['remote_data']['access_generated_at'];
$this->expires_in = intval($data['expires_in']);
}
return $data['access_token'];
}
function rename($from, $to){
global $error;
$stream_from = parse_url($from);
$this->refresh_token = $stream_from['host'];
$from_path = trim($stream_from['path'], '/\\');
$from_pathinfo = pathinfo($stream_from['path']);
$from_file = $from_pathinfo['basename'];
$stream_to = parse_url($to);
$to_path = trim($stream_to['path'], '/\\');
$to_pathinfo = pathinfo($stream_to['path']);
$to_file = $to_pathinfo['basename'];
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
$this->get_gdrive_fileid($from_file);
$post = json_encode(array('name' => $to_file));
$url = 'https://www.googleapis.com/drive/v3/files/'.$this->gdrive_fileid;
$headers = array('Authorization: Bearer '.$this->access_token,
'Content-Type: application/json; charset=UTF-8',
'X-Upload-Content-Type: application/x-gzip');
$resp = $this->__curl($url, $headers, '', 0, $post, '', 'PATCH');
if(!empty($resp['error'])){
$error[] = 'Google Drive : '.$resp['error'];
return false;
}
return true;
}
//Download Backup File from Google Drive to local server
function download_file_loop($source, $dest, $startpos = 0){
// backuply_log(' inside download_file_loop ');
// backuply_log('source : '.$source);
// backuply_log(' dest : '.$dest);
// backuply_log(' dest : '.$startpos);
global $error;
//Set chunk size for download as 2 MB
$this->chunk = 2097152;
$stream = parse_url($source);
$this->refresh_token = $stream['host'];
$src_file = trim($stream['path'], '\//');
//Google Drive access token expires in an hour so we need to refresh
$this->access_token = $this->refresh_token_func($this->refresh_token);
$pathinfo = pathinfo($stream['path']);
$src_file = $pathinfo['basename'];
$this->get_gdrive_fileid($src_file);
$file_stats = $this->url_stat($source);
$this->filesize = !empty($file_stats) ? $file_stats['size'] : 0;
$this->range_lower_limit = $startpos;
$this->range_upper_limit = ($this->range_lower_limit + $this->chunk) - 1;
$fp = @fopen($dest, 'ab');
while(!$this->__eof()){
if(time() + 5 >= $GLOBALS['end']){
$GLOBALS['l_readbytes'] = filesize($dest);
$GLOBALS['remote_data']['access_token'] = $this->access_token;
$GLOBALS['remote_data']['fileid'] = $this->cache['fileid'];
$GLOBALS['remote_data']['stats'] = $this->cache['stats'];
break;
}
if($this->range_upper_limit >= $this->filesize){
$this->range_upper_limit = $this->filesize - 1;
}
$block = $this->__read($this->range_lower_limit, $this->range_upper_limit);
fwrite($fp, $block);
$this->offset = $this->range_upper_limit + 1;
$this->range_lower_limit = $this->range_upper_limit + 1;
$this->range_upper_limit = ($this->range_lower_limit + $this->chunk) - 1;
$percentage = (filesize($dest) / $this->filesize) * 100;
backuply_status_log('