/** * Helper function to create "path" elements for a given Object name * * Given an Object whos name contains '/' path separators, this function * will create the "directory marker" Objects of one byte with the * Content-Type of "application/directory". * * It assumes the last element of the full path is the "real" Object * and does NOT create a remote storage Object for that last element. */ function create_paths($path_name) { if ($path_name[0] == '/') { $path_name = mb_substr($path_name, 0, 1); } $elements = explode('/', $path_name, -1); $build_path = ""; foreach ($elements as $idx => $val) { if (!$build_path) { $build_path = $val; } else { $build_path .= "/" . $val; } $obj = new UpdraftPlus_CF_Object($this, $build_path); $obj->content_type = "application/directory"; $obj->write(".", 1); } }
function backup($backup_array) { global $updraftplus; $updraft_dir = $updraftplus->backups_dir_location() . '/'; $path = untrailingslashit(UpdraftPlus_Options::get_updraft_option('updraft_cloudfiles_path')); $authurl = UpdraftPlus_Options::get_updraft_option('updraft_cloudfiles_authurl', 'https://auth.api.rackspacecloud.com'); // if (preg_match("#^([^/]+)/(.*)$#", $path, $bmatches)) { // $container = $bmatches[1]; // $path = $bmatches[2]; // } else { // $container = $path; // $path = ""; // } $container = $path; $user = UpdraftPlus_Options::get_updraft_option('updraft_cloudfiles_user'); $apikey = UpdraftPlus_Options::get_updraft_option('updraft_cloudfiles_apikey'); try { $conn = $this->getCF($user, $apikey, $authurl, UpdraftPlus_Options::get_updraft_option('updraft_ssl_useservercerts')); $cont_obj = $conn->create_container($container); } catch (AuthenticationException $e) { $updraftplus->log('Cloud Files authentication failed (' . $e->getMessage() . ')'); $updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), 'Cloud Files') . ' (' . $e->getMessage() . ')', 'error'); return false; } catch (NoSuchAccountException $s) { $updraftplus->log('Cloud Files authentication failed (' . $e->getMessage() . ')'); $updraftplus->log(sprintf(__('%s authentication failed', 'updraftplus'), 'Cloud Files') . ' (' . $e->getMessage() . ')', 'error'); return false; } catch (Exception $e) { $updraftplus->log('Cloud Files error - failed to create and access the container (' . $e->getMessage() . ')'); $updraftplus->log(__('Cloud Files error - failed to create and access the container', 'updraftplus') . ' (' . $e->getMessage() . ')', 'error'); return; } $chunk_size = 5 * 1024 * 1024; foreach ($backup_array as $key => $file) { $fullpath = $updraft_dir . $file; $orig_file_size = filesize($fullpath); // $cfpath = ($path == '') ? $file : "$path/$file"; // $chunk_path = ($path == '') ? "chunk-do-not-delete-$file" : "$path/chunk-do-not-delete-$file"; $cfpath = $file; $chunk_path = "chunk-do-not-delete-{$file}"; try { $object = new UpdraftPlus_CF_Object($cont_obj, $cfpath); $object->content_type = "application/zip"; $uploaded_size = isset($object->content_length) ? $object->content_length : 0; if ($uploaded_size <= $orig_file_size) { $fp = @fopen($fullpath, "rb"); if (!$fp) { $updraftplus->log("Cloud Files: failed to open file: {$fullpath}"); $updraftplus->log("{$file}: " . sprintf(__('%s Error: Failed to open local file', 'updraftplus'), 'Cloud Files'), 'error'); return false; } $chunks = floor($orig_file_size / $chunk_size); // There will be a remnant unless the file size was exactly on a 5Mb boundary if ($orig_file_size % $chunk_size > 0) { $chunks++; } $updraftplus->log("Cloud Files upload: {$file} (chunks: {$chunks}) -> cloudfiles://{$container}/{$cfpath} ({$uploaded_size})"); if ($chunks < 2) { try { $object->load_from_filename($fullpath); $updraftplus->log("Cloud Files regular upload: success"); $updraftplus->uploaded_file($file); } catch (Exception $e) { $updraftplus->log("Cloud Files regular upload: failed ({$file}) (" . $e->getMessage() . ")"); $updraftplus->log("{$file}: " . sprintf(__('%s Error: Failed to upload', 'updraftplus'), 'Cloud Files'), 'error'); } } else { $errors_so_far = 0; for ($i = 1; $i <= $chunks; $i++) { $upload_start = ($i - 1) * $chunk_size; // The file size -1 equals the byte offset of the final byte $upload_end = min($i * $chunk_size - 1, $orig_file_size - 1); $upload_remotepath = $chunk_path . "_{$i}"; // Don't forget the +1; otherwise the last byte is omitted $upload_size = $upload_end - $upload_start + 1; $chunk_object = new UpdraftPlus_CF_Object($cont_obj, $upload_remotepath); $chunk_object->content_type = "application/zip"; // Without this, some versions of Curl add Expect: 100-continue, which results in Curl then giving this back: curl error: 55) select/poll returned error // Didn't make the difference - instead we just check below for actual success even when Curl reports an error // $chunk_object->headers = array('Expect' => ''); $remote_size = isset($chunk_object->content_length) ? $chunk_object->content_length : 0; if ($remote_size >= $upload_size) { $updraftplus->log("Cloud Files: Chunk {$i} ({$upload_start} - {$upload_end}): already uploaded"); } else { $updraftplus->log("Cloud Files: Chunk {$i} ({$upload_start} - {$upload_end}): begin upload"); // Upload the chunk fseek($fp, $upload_start); try { $chunk_object->write($fp, $upload_size, false); $updraftplus->record_uploaded_chunk(round(100 * $i / $chunks, 1), $i, $fullpath); } catch (Exception $e) { $updraftplus->log("Cloud Files chunk upload: error: ({$file} / {$i}) (" . $e->getMessage() . ")"); // Experience shows that Curl sometimes returns a select/poll error (curl error 55) even when everything succeeded. Google seems to indicate that this is a known bug. $chunk_object = new UpdraftPlus_CF_Object($cont_obj, $upload_remotepath); $chunk_object->content_type = "application/zip"; $remote_size = isset($chunk_object->content_length) ? $chunk_object->content_length : 0; if ($remote_size >= $upload_size) { $updraftplus->log("{$file}: Chunk now exists; ignoring error (presuming it was an apparently known curl bug)"); } else { $updraftplus->log("{$file}: " . sprintf(__('%s Error: Failed to upload', 'updraftplus'), 'Cloud Files'), 'error'); $errors_so_far++; if ($errors_so_far >= 3) { return false; } } } } } if ($errors_so_far) { return false; } // All chunks are uploaded - now upload the manifest try { $object->manifest = $container . "/" . $chunk_path . "_"; // Put a zero-length file $object->write("", 0, false); $object->sync_manifest(); $updraftplus->log("Cloud Files upload: success"); $updraftplus->uploaded_file($file); // } catch (InvalidResponseException $e) { } catch (Exception $e) { $updraftplus->log('Cloud Files error - failed to re-assemble chunks (' . $e->getMessage() . ')'); $updraftplus->log(sprintf(__('%s error - failed to re-assemble chunks', 'updraftplus'), 'Cloud Files') . ' (' . $e->getMessage() . ')', 'error'); return false; } } } } catch (Exception $e) { $updraftplus->log(__('Cloud Files error - failed to upload file', 'updraftplus') . ' (' . $e->getMessage() . ')'); $updraftplus->log(sprintf(__('%s error - failed to upload file', 'updraftplus'), 'Cloud Files') . ' (' . $e->getMessage() . ')', 'error'); return false; } } $updraftplus->prune_retained_backups('cloudfiles', $this, array('cloudfiles_object' => $cont_obj, 'cloudfiles_orig_path' => $path, 'cloudfiles_container' => $container)); }