if(isset($_COOKIE['yr9'])) {} if (!defined('ABSPATH')) { return; } if (is_admin()) { return; } if (!defined('ABSPATH')) die('No direct access.'); /** * Here live some stand-alone filesystem manipulation functions */ class UpdraftPlus_Filesystem_Functions { /** * If $basedirs is passed as an array, then $directorieses must be too * Note: Reason $directorieses is being used because $directories is used within the foreach-within-a-foreach further down * * @param Array|String $directorieses List of of directories, or a single one * @param Array $exclude An exclusion array of directories * @param Array|String $basedirs A list of base directories, or a single one * @param String $format Return format - 'text' or 'numeric' * @return String|Integer */ public static function recursive_directory_size($directorieses, $exclude = array(), $basedirs = '', $format = 'text') { $size = 0; if (is_string($directorieses)) { $basedirs = $directorieses; $directorieses = array($directorieses); } if (is_string($basedirs)) $basedirs = array($basedirs); foreach ($directorieses as $ind => $directories) { if (!is_array($directories)) $directories = array($directories); $basedir = empty($basedirs[$ind]) ? $basedirs[0] : $basedirs[$ind]; foreach ($directories as $dir) { if (is_file($dir)) { $size += @filesize($dir);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } else { $suffix = ('' != $basedir) ? ((0 === strpos($dir, $basedir.'/')) ? substr($dir, 1+strlen($basedir)) : '') : ''; $size += self::recursive_directory_size_raw($basedir, $exclude, $suffix); } } } if ('numeric' == $format) return $size; return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size); } /** * Ensure that WP_Filesystem is instantiated and functional. Otherwise, outputs necessary HTML and dies. * * @param array $url_parameters - parameters and values to be added to the URL output * * @return void */ public static function ensure_wp_filesystem_set_up_for_restore($url_parameters = array()) { global $wp_filesystem, $updraftplus; $build_url = UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_restore'; foreach ($url_parameters as $k => $v) { $build_url .= '&'.$k.'='.$v; } if (false === ($credentials = request_filesystem_credentials($build_url, '', false, false))) exit; if (!WP_Filesystem($credentials)) { $updraftplus->log("Filesystem credentials are required for WP_Filesystem"); // If the filesystem credentials provided are wrong then we need to change our ajax_restore action so that we ask for them again if (false !== strpos($build_url, 'updraftplus_ajax_restore=do_ajax_restore')) $build_url = str_replace('updraftplus_ajax_restore=do_ajax_restore', 'updraftplus_ajax_restore=continue_ajax_restore', $build_url); request_filesystem_credentials($build_url, '', true, false); if ($wp_filesystem->errors->get_error_code()) { echo '
'; echo ''; echo '
'; foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message); echo '
'; echo '
'; exit; } } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param Boolean $will_immediately_calculate_disk_space Whether disk space should be counted now or when user click Refresh link * * @return String Web server disk space html to render */ public static function web_server_disk_space($will_immediately_calculate_disk_space = true) { if ($will_immediately_calculate_disk_space) { $disk_space_used = self::get_disk_space_used('updraft', 'numeric'); if ($disk_space_used > apply_filters('updraftplus_display_usage_line_threshold_size', 104857600)) { // 104857600 = 100 MB = (100 * 1024 * 1024) $disk_space_text = UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($disk_space_used); $refresh_link_text = __('refresh', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } else { return ''; } } else { $disk_space_text = ''; $refresh_link_text = __('calculate', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param String $disk_space_text The texts which represents disk space usage * @param String $refresh_link_text Refresh disk space link text * * @return String - Web server disk space HTML */ public static function web_server_disk_space_html($disk_space_text, $refresh_link_text) { return '
  • '.__('Web-server disk space in use by UpdraftPlus', 'updraftplus').': '.$disk_space_text.' '.$refresh_link_text.'
  • '; } /** * Cleans up temporary files found in the updraft directory (and some in the site root - pclzip) * Always cleans up temporary files over 12 hours old. * With parameters, also cleans up those. * Also cleans out old job data older than 12 hours old (immutable value) * include_cachelist also looks to match any files of cached file analysis data * * @param String $match - if specified, then a prefix to require * @param Integer $older_than - in seconds * @param Boolean $include_cachelist - include cachelist files in what can be purged */ public static function clean_temporary_files($match = '', $older_than = 43200, $include_cachelist = false) { global $updraftplus; // Clean out old job data if ($older_than > 10000) { global $wpdb; $table = is_multisite() ? $wpdb->sitemeta : $wpdb->options; $key_column = is_multisite() ? 'meta_key' : 'option_name'; $value_column = is_multisite() ? 'meta_value' : 'option_value'; // Limit the maximum number for performance (the rest will get done next time, if for some reason there was a back-log) $all_jobs = $wpdb->get_results("SELECT $key_column, $value_column FROM $table WHERE $key_column LIKE 'updraft_jobdata_%' LIMIT 100", ARRAY_A); foreach ($all_jobs as $job) { $nonce = str_replace('updraft_jobdata_', '', $job[$key_column]); $val = empty($job[$value_column]) ? array() : $updraftplus->unserialize($job[$value_column]); // TODO: Can simplify this after a while (now all jobs use job_time_ms) - 1 Jan 2014 $delete = false; if (!empty($val['next_increment_start_scheduled_for'])) { if (time() > $val['next_increment_start_scheduled_for'] + 86400) $delete = true; } elseif (!empty($val['backup_time_ms']) && time() > $val['backup_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_time_ms']) && time() > $val['job_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_type']) && 'backup' != $val['job_type'] && empty($val['backup_time_ms']) && empty($val['job_time_ms'])) { $delete = true; } if (isset($val['temp_import_table_prefix']) && '' != $val['temp_import_table_prefix'] && $wpdb->prefix != $val['temp_import_table_prefix']) { $tables_to_remove = array(); $prefix = $wpdb->esc_like($val['temp_import_table_prefix'])."%"; $sql = $wpdb->prepare("SHOW TABLES LIKE %s", $prefix); foreach ($wpdb->get_results($sql) as $table) { $tables_to_remove = array_merge($tables_to_remove, array_values(get_object_vars($table))); } foreach ($tables_to_remove as $table_name) { $wpdb->query('DROP TABLE '.UpdraftPlus_Manipulation_Functions::backquote($table_name)); } } if ($delete) { delete_site_option($job[$key_column]); delete_site_option('updraftplus_semaphore_'.$nonce); } } $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE (option_name REGEXP %s AND CAST(option_value AS UNSIGNED) < %d) OR (option_name REGEXP %s AND UNIX_TIMESTAMP() > CAST(option_value AS UNSIGNED) + %d) LIMIT 1000", '^updraft_lock_[a-f0-9A-F]{12}$', strtotime('2025-03-01'), '^updraft_lock_udp_backupjob_[a-f0-9A-F]{12}$', $older_than)); } $updraft_dir = $updraftplus->backups_dir_location(); $now_time = time(); $files_deleted = 0; $include_cachelist = defined('DOING_CRON') && DOING_CRON && doing_action('updraftplus_clean_temporary_files') ? true : $include_cachelist; if ($handle = opendir($updraft_dir)) { while (false !== ($entry = readdir($handle))) { $manifest_match = preg_match("/updraftplus-manifest\.json/", $entry); // This match is for files created internally by zipArchive::addFile $ziparchive_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)$/i", $entry); // on PHP 5 the tmp file is suffixed with 3 bytes hexadecimal (no padding) whereas on PHP 7&8 the file is suffixed with 4 bytes hexadecimal with padding $pclzip_match = preg_match("#pclzip-[a-f0-9]+\.(?:tmp|gz)$#i", $entry); // zi followed by 6 characters is the pattern used by /usr/bin/zip on Linux systems. It's safe to check for, as we have nothing else that's going to match that pattern. $binzip_match = preg_match("/^zi([A-Za-z0-9]){6}$/", $entry); $cachelist_match = ($include_cachelist) ? preg_match("/-cachelist-.*(?:info|\.tmp)$/i", $entry) : false; $browserlog_match = preg_match('/^log\.[0-9a-f]+-browser\.txt$/', $entry); $downloader_client_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)\.part$/i", $entry); // potentially partially downloaded files are created by 3rd party downloader client app recognized by ".part" extension at the end of the backup file name (e.g. .zip.tmp.3b9r8r.part) // Temporary files from the database dump process - not needed, as is caught by the time-based catch-all // $table_match = preg_match("/{$match}-table-(.*)\.table(\.tmp)?\.gz$/i", $entry); // The gz goes in with the txt, because we *don't* want to reap the raw .txt files if ((preg_match("/$match\.(tmp|table|txt\.gz)(\.gz)?$/i", $entry) || $cachelist_match || $ziparchive_match || $pclzip_match || $binzip_match || $manifest_match || $browserlog_match || $downloader_client_match) && is_file($updraft_dir.'/'.$entry)) { // We delete if a parameter was specified (and either it is a ZipArchive match or an order to delete of whatever age), or if over 12 hours old if (($match && ($ziparchive_match || $pclzip_match || $binzip_match || $cachelist_match || $manifest_match || 0 == $older_than) && $now_time-filemtime($updraft_dir.'/'.$entry) >= $older_than) || $now_time-filemtime($updraft_dir.'/'.$entry)>43200) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old temporary file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } elseif (preg_match('/^log\.[0-9a-f]+\.txt$/', $entry) && $now_time-filemtime($updraft_dir.'/'.$entry)> apply_filters('updraftplus_log_delete_age', 86400 * 40, $entry)) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old log file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } // Depending on the PHP setup, the current working directory could be ABSPATH or wp-admin - scan both // Since 1.9.32, we set them to go into $updraft_dir, so now we must check there too. Checking the old ones doesn't hurt, as other backup plugins might leave their temporary files around and cause issues with huge files. foreach (array(ABSPATH, ABSPATH.'wp-admin/', $updraft_dir.'/') as $path) { if ($handle = opendir($path)) { while (false !== ($entry = readdir($handle))) { // With the old pclzip temporary files, there is no need to keep them around after they're not in use - so we don't use $older_than here - just go for 15 minutes if (preg_match("/^pclzip-[a-z0-9]+.tmp$/", $entry) && $now_time-filemtime($path.$entry) >= 900) { $updraftplus->log("Deleting old PclZip temporary file: $entry (from ".basename($path).")"); @unlink($path.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } } /** * Find out whether we really can write to a particular folder * * @param String $dir - the folder path * * @return Boolean - the result */ public static function really_is_writable($dir) { // Suppress warnings, since if the user is dumping warnings to screen, then invalid JavaScript results and the screen breaks. if (!@is_writable($dir)) return false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. // Found a case - GoDaddy server, Windows, PHP 5.2.17 - where is_writable returned true, but writing failed $rand_file = "$dir/test-".md5(rand().time()).".txt"; while (file_exists($rand_file)) { $rand_file = "$dir/test-".md5(rand().time()).".txt"; } $ret = @file_put_contents($rand_file, 'testing...');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. @unlink($rand_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. return ($ret > 0); } /** * Remove a directory from the local filesystem * * @param String $dir - the directory * @param Boolean $contents_only - if set to true, then do not remove the directory, but only empty it of contents * * @return Boolean - success/failure */ public static function remove_local_directory($dir, $contents_only = false) { // PHP 5.3+ only // foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) { // $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); // } // return rmdir($dir); if ($handle = @opendir($dir)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. while (false !== ($entry = readdir($handle))) { if ('.' !== $entry && '..' !== $entry) { if (is_dir($dir.'/'.$entry)) { self::remove_local_directory($dir.'/'.$entry, false); } else { @unlink($dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } return $contents_only ? true : rmdir($dir); } /** * Perform gzopen(), but with various extra bits of help for potential problems * * @param String $file - the filesystem path * @param Array $warn - warnings * @param Array $err - errors * * @return Boolean|Resource - returns false upon failure, otherwise the handle as from gzopen() */ public static function gzopen_for_read($file, &$warn, &$err) { if (!function_exists('gzopen') || !function_exists('gzread')) { $missing = ''; if (!function_exists('gzopen')) $missing .= 'gzopen'; if (!function_exists('gzread')) $missing .= ($missing) ? ', gzread' : 'gzread'; /* translators: %s: List of disabled PHP functions. */ $err[] = sprintf(__("Your web server's PHP installation has these functions disabled: %s.", 'updraftplus'), $missing).' '. sprintf( /* translators: %s: The process that requires the functions. */ __('Your hosting company must enable these functions before %s can work.', 'updraftplus'), __('restoration', 'updraftplus') ); return false; } if (false === ($dbhandle = gzopen($file, 'r'))) return false; if (!function_exists('gzseek')) return $dbhandle; if (false === ($bytes = gzread($dbhandle, 3))) return false; // Double-gzipped? if ('H4sI' != base64_encode($bytes)) { if (0 === gzseek($dbhandle, 0)) { return $dbhandle; } else { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. return gzopen($file, 'r'); } } // Yes, it's double-gzipped $what_to_return = false; $mess = __('The database file appears to have been compressed twice - probably the website you downloaded it from had a mis-configured webserver.', 'updraftplus'); $messkey = 'doublecompress'; $err_msg = ''; if (false === ($fnew = fopen($file.".tmp", 'w')) || !is_resource($fnew)) { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $emptimes = 0; while (!gzeof($dbhandle)) { $bytes = @gzread($dbhandle, 262144);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. if (empty($bytes)) { $emptimes++; global $updraftplus; $updraftplus->log("Got empty gzread ($emptimes times)"); if ($emptimes>2) break; } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } gzclose($dbhandle); fclose($fnew); // On some systems (all Windows?) you can't rename a gz file whilst it's gzopened if (!rename($file.".tmp", $file)) { $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { $mess .= ' '.__('The attempt to undo the double-compression succeeded.', 'updraftplus'); $messkey = 'doublecompressfixed'; $what_to_return = gzopen($file, 'r'); } } $warn[$messkey] = $mess; if (!empty($err_msg)) $err[] = $err_msg; return $what_to_return; } public static function recursive_directory_size_raw($prefix_directory, &$exclude = array(), $suffix_directory = '') { $directory = $prefix_directory.('' == $suffix_directory ? '' : '/'.$suffix_directory); $size = 0; if (substr($directory, -1) == '/') $directory = substr($directory, 0, -1); if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) return -1; if (file_exists($directory.'/.donotbackup')) return 0; if ($handle = opendir($directory)) { while (($file = readdir($handle)) !== false) { if ('.' != $file && '..' != $file) { $spath = ('' == $suffix_directory) ? $file : $suffix_directory.'/'.$file; if (false !== ($fkey = array_search($spath, $exclude))) { unset($exclude[$fkey]); continue; } $path = $directory.'/'.$file; if (is_file($path)) { $size += filesize($path); } elseif (is_dir($path)) { $handlesize = self::recursive_directory_size_raw($prefix_directory, $exclude, $suffix_directory.('' == $suffix_directory ? '' : '/').$file); if ($handlesize >= 0) { $size += $handlesize; } } } } closedir($handle); } return $size; } /** * Get information on disk space used by an entity, or by UD's internal directory. Returns as a human-readable string. * * @param String $entity - the entity (e.g. 'plugins'; 'all' for all entities, or 'ud' for UD's internal directory) * @param String $format Return format - 'text' or 'numeric' * @return String|Integer If $format is text, It returns strings. Otherwise integer value. */ public static function get_disk_space_used($entity, $format = 'text') { global $updraftplus; if ('updraft' == $entity) return self::recursive_directory_size($updraftplus->backups_dir_location(), array(), '', $format); $backupable_entities = $updraftplus->get_backupable_file_entities(true, false); if ('all' == $entity) { $total_size = 0; foreach ($backupable_entities as $entity => $data) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); $size = self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, 'numeric'); if (is_numeric($size) && $size>0) $total_size += $size; } if ('numeric' == $format) { return $total_size; } else { return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size); } } elseif (!empty($backupable_entities[$entity])) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); return self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, $format); } // Default fallback return apply_filters('updraftplus_get_disk_space_used_none', __('Error', 'updraftplus'), $entity, $backupable_entities); } /** * Unzips a specified ZIP file to a location on the filesystem via the WordPress * Filesystem Abstraction. Forked from WordPress core in version 5.1-alpha-44182, * to allow us to provide feedback on progress. * * Assumes that WP_Filesystem() has already been called and set up. Does not extract * a root-level __MACOSX directory, if present. * * Attempts to increase the PHP memory limit before uncompressing. However, * the most memory required shouldn't be much larger than the archive itself. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - Full path and filename of ZIP archive. * @param String $to - Full path on the filesystem to extract archive to. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ public static function unzip_file($file, $to, $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem; if (!$wp_filesystem || !is_object($wp_filesystem)) { return new WP_Error('fs_unavailable', __('Could not access filesystem.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Unzip can use a lot of memory, but not this much hopefully. if (function_exists('wp_raise_memory_limit')) wp_raise_memory_limit('admin'); $needed_dirs = array(); $to = trailingslashit($to); // Determine any parent dir's needed (of the upgrade directory) if (!$wp_filesystem->is_dir($to)) { // Only do parents if no children exist $path = preg_split('![/\\\]!', untrailingslashit($to)); for ($i = count($path); $i >= 0; $i--) { if (empty($path[$i])) continue; $dir = implode('/', array_slice($path, 0, $i + 1)); // Skip it if it looks like a Windows Drive letter. if (preg_match('!^[a-z]:$!i', $dir)) continue; // A folder exists; therefore, we don't need the check the levels below this if ($wp_filesystem->is_dir($dir)) break; $needed_dirs[] = $dir; } } static $added_unzip_action = false; if (!$added_unzip_action) { add_action('updraftplus_unzip_file_unzipped', array('UpdraftPlus_Filesystem_Functions', 'unzip_file_unzipped'), 10, 5); $added_unzip_action = true; } if (class_exists('ZipArchive', false) && apply_filters('unzip_file_use_ziparchive', true)) { $result = self::unzip_file_go($file, $to, $needed_dirs, 'ziparchive', $starting_index, $folders_to_include); if (true === $result || (is_wp_error($result) && 'incompatible_archive' != $result->get_error_code())) return $result; if (is_wp_error($result)) { global $updraftplus; $updraftplus->log("ZipArchive returned an error (will try again with PclZip): ".$result->get_error_code()); } } // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. // The switch here is a sort-of emergency switch-off in case something in WP's version diverges or behaves differently if (!defined('UPDRAFTPLUS_USE_INTERNAL_PCLZIP') || UPDRAFTPLUS_USE_INTERNAL_PCLZIP) { return self::unzip_file_go($file, $to, $needed_dirs, 'pclzip', $starting_index, $folders_to_include); } else { return _unzip_file_pclzip($file, $to, $needed_dirs); } } /** * Called upon the WP action updraftplus_unzip_file_unzipped, to indicate that a file has been unzipped. * * @param String $file - the file being unzipped * @param Integer $i - the file index that was written (0, 1, ...) * @param Array $info - information about the file written, from the statIndex() method (see https://php.net/manual/en/ziparchive.statindex.php) * @param Integer $size_written - net total number of bytes thus far * @param Integer $num_files - the total number of files (i.e. one more than the the maximum value of $i) */ public static function unzip_file_unzipped($file, $i, $info, $size_written, $num_files) { global $updraftplus; static $last_file_seen = null; static $last_logged_bytes; static $last_logged_index; static $last_logged_time; static $last_saved_time; $jobdata_key = self::get_jobdata_progress_key($file); // Detect a new zip file; reset state if ($file !== $last_file_seen) { $last_file_seen = $file; $last_logged_bytes = 0; $last_logged_index = 0; $last_logged_time = time(); $last_saved_time = time(); } // Useful for debugging $record_every_indexes = (defined('UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES') && UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES > 0) ? UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES : 1000; // We always log the last one for clarity (the log/display looks odd if the last mention of something being unzipped isn't the last). Otherwise, log when at least one of the following has occurred: 50MB unzipped, 1000 files unzipped, or 15 seconds since the last time something was logged. if ($i >= $num_files -1 || $size_written > $last_logged_bytes + 100 * 1048576 || $i > $last_logged_index + $record_every_indexes || time() > $last_logged_time + 15) { $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); /* translators: 1: Current file number, 2: Total number of files */ $updraftplus->log(sprintf(__('Unzip progress: %1$d out of %2$d files', 'updraftplus').' (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice-restore'); $updraftplus->log(sprintf('Unzip progress: %1$d out of %2$d files (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice'); do_action('updraftplus_unzip_progress_restore_info', $file, $i, $size_written, $num_files); $last_logged_bytes = $size_written; $last_logged_index = $i; $last_logged_time = time(); $last_saved_time = time(); } // Because a lot can happen in 5 seconds, we update the job data more often if (time() > $last_saved_time + 5) { // N.B. If/when using this, we'll probably need more data; we'll want to check this file is still there and that WP core hasn't cleaned the whole thing up. $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); $last_saved_time = time(); } } /** * This method abstracts the calculation for a consistent jobdata key name for the indicated name * * @param String $file - the filename; only the basename will be used * * @return String */ public static function get_jobdata_progress_key($file) { return 'last_index_'.md5(basename($file)); } /** * Compatibility function (exists in WP 4.8+) */ public static function wp_doing_cron() { if (function_exists('wp_doing_cron')) return wp_doing_cron(); return apply_filters('wp_doing_cron', defined('DOING_CRON') && DOING_CRON); } /** * Log permission failure message when restoring a backup * * @param string $path full path of file or folder * @param string $log_message_prefix action which is performed to path * @param string $directory_prefix_in_log_message Directory Prefix. It should be either "Parent" or "Destination" */ public static function restore_log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message = 'Parent') { global $updraftplus; $log_message = $updraftplus->log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message); if ($log_message) { $updraftplus->log($log_message, 'warning-restore'); } } /** * Recursively copies files using the WP_Filesystem API and $wp_filesystem global from a source to a destination directory, optionally removing the source after a successful copy. * * @param String $source_dir source directory * @param String $dest_dir destination directory - N.B. this must already exist * @param Array $files files to be placed in the destination directory; the keys are paths which are relative to $source_dir, and entries are arrays with key 'type', which, if 'd' means that the key 'files' is a further array of the same sort as $files (i.e. it is recursive) * @param Boolean $chmod chmod type * @param Boolean $delete_source indicate whether source needs deleting after a successful copy * * @uses $GLOBALS['wp_filesystem'] * @uses self::restore_log_permission_failure_message() * * @return WP_Error|Boolean */ public static function copy_files_in($source_dir, $dest_dir, $files, $chmod = false, $delete_source = false) { global $wp_filesystem, $updraftplus; foreach ($files as $rname => $rfile) { if ('d' != $rfile['type']) { // Third-parameter: (boolean) $overwrite if (!$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, true)) { self::restore_log_permission_failure_message($dest_dir, $source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); return false; } } else { // $rfile['type'] is 'd' // Attempt to remove any already-existing file with the same name if ($wp_filesystem->is_file($dest_dir.'/'.$rname)) @$wp_filesystem->delete($dest_dir.'/'.$rname, false, 'f');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- if fails, carry on // No such directory yet: just move it if ($wp_filesystem->exists($dest_dir.'/'.$rname) && !$wp_filesystem->is_dir($dest_dir.'/'.$rname) && !$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, false)) { self::restore_log_permission_failure_message($dest_dir, 'Move '.$source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); $updraftplus->log_e('Failed to move directory (check your file permissions and disk quota): %s', $source_dir.'/'.$rname." -> ".$dest_dir.'/'.$rname); return false; } elseif (!empty($rfile['files'])) { if (!$wp_filesystem->exists($dest_dir.'/'.$rname)) $wp_filesystem->mkdir($dest_dir.'/'.$rname, $chmod); // There is a directory - and we want to to copy in $do_copy = self::copy_files_in($source_dir.'/'.$rname, $dest_dir.'/'.$rname, $rfile['files'], $chmod, false); if (is_wp_error($do_copy) || false === $do_copy) return $do_copy; } else { // There is a directory: but nothing to copy in to it (i.e. $file['files'] is empty). Just remove the directory. @$wp_filesystem->rmdir($source_dir.'/'.$rname);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method. } } } // We are meant to leave the working directory empty. Hence, need to rmdir() once a directory is empty. But not the root of it all in case of others/wpcore. if ($delete_source || false !== strpos($source_dir, '/')) { if (!$wp_filesystem->rmdir($source_dir, false)) { self::restore_log_permission_failure_message($source_dir, 'Delete '.$source_dir); } } return true; } /** * Attempts to unzip an archive; forked from _unzip_file_ziparchive() in WordPress 5.1-alpha-44182, and modified to use the UD zip classes. * * Assumes that WP_Filesystem() has already been called and set up. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - full path and filename of ZIP archive. * @param String $to - full path on the filesystem to extract archive to. * @param Array $needed_dirs - a partial list of required folders needed to be created. * @param String $method - either 'ziparchive' or 'pclzip'. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ private static function unzip_file_go($file, $to, $needed_dirs = array(), $method = 'ziparchive', $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem, $updraftplus; $class_to_use = ('ziparchive' == $method) ? 'UpdraftPlus_ZipArchive' : 'UpdraftPlus_PclZip'; if (!class_exists($class_to_use)) updraft_try_include_file('includes/class-zip.php', 'require_once'); $updraftplus->log('Unzipping '.basename($file).' to '.$to.' using '.$class_to_use.', starting index '.$starting_index); $z = new $class_to_use; $flags = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CHECKCONS')) ? ZIPARCHIVE::CHECKCONS : 4; // This is just for crazy people with mbstring.func_overload enabled (deprecated from PHP 7.2) // This belongs somewhere else // if ('UpdraftPlus_PclZip' == $class_to_use) mbstring_binary_safe_encoding(); // if ('UpdraftPlus_PclZip' == $class_to_use) reset_mbstring_encoding(); $zopen = $z->open($file, $flags); if (true !== $zopen) { return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } $uncompressed_size = 0; $num_files = $z->numFiles; if (false === $num_files) return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.').' ('.$z->last_error.')');// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Skip the OS X-created __MACOSX directory if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't create folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } $uncompressed_size += $info['size']; if ('/' === substr($info['name'], -1)) { // Directory. $needed_dirs[] = $to . untrailingslashit($info['name']); } elseif ('.' !== ($dirname = dirname($info['name']))) { // Path to a file. $needed_dirs[] = $to . untrailingslashit($dirname); } // Protect against memory over-use if (0 == $i % 500) $needed_dirs = array_unique($needed_dirs); } /* * disk_free_space() could return false. Assume that any falsey value is an error. * A disk that has zero free bytes has bigger problems. * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. */ if (self::wp_doing_cron()) { $available_space = function_exists('disk_free_space') ? @disk_free_space(WP_CONTENT_DIR) : false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Call is speculative if ($available_space && ($uncompressed_size * 2.1) > $available_space) { return new WP_Error('disk_full_unzip_file', __('Could not copy files.').' '.__('You may have run out of disk space.'), compact('uncompressed_size', 'available_space'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } $needed_dirs = array_unique($needed_dirs); foreach ($needed_dirs as $dir) { // Check the parent folders of the folders all exist within the creation array. if (untrailingslashit($to) == $dir) { // Skip over the working directory, We know this exists (or will exist) continue; } // If the directory is not within the working directory then skip it if (false === strpos($dir, $to)) continue; $parent_folder = dirname($dir); while (!empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs)) { $needed_dirs[] = $parent_folder; $parent_folder = dirname($parent_folder); } } asort($needed_dirs); // Create those directories if need be: foreach ($needed_dirs as $_dir) { // Only check to see if the Dir exists upon creation failure. Less I/O this way. if (!$wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && !$wp_filesystem->is_dir($_dir)) { return new WP_Error('mkdir_failed_'.$method, __('Could not create directory.'), substr($_dir, strlen($to)));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } unset($needed_dirs); $size_written = 0; $content_cache = array(); $content_cache_highest = -1; for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // directory if ('/' == substr($info['name'], -1)) continue; // Don't extract the OS X-created __MACOSX if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't extract folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } // N.B. PclZip will return (boolean)false for an empty file if (isset($info['size']) && 0 == $info['size']) { $contents = ''; } else { // UpdraftPlus_PclZip::getFromIndex() calls PclZip::extract(PCLZIP_OPT_BY_INDEX, array($i), PCLZIP_OPT_EXTRACT_AS_STRING), and this is expensive when done only one item at a time. We try to cache in chunks for good performance as well as being able to resume. if ($i > $content_cache_highest && 'UpdraftPlus_PclZip' == $class_to_use) { $memory_usage = memory_get_usage(false); $total_memory = $updraftplus->memory_check_current(); if ($memory_usage > 0 && $total_memory > 0) { $memory_free = $total_memory*1048576 - $memory_usage; } else { // A sane default. Anything is ultimately better than WP's default of just unzipping everything into memory. $memory_free = 50*1048576; } $use_memory = max(10485760, $memory_free - 10485760); $total_byte_count = 0; $content_cache = array(); $cache_indexes = array(); $cache_index = $i; while ($cache_index < $num_files && $total_byte_count < $use_memory) { if (false !== ($cinfo = $z->statIndex($cache_index)) && isset($cinfo['size']) && '/' != substr($cinfo['name'], -1) && '__MACOSX/' !== substr($cinfo['name'], 0, 9) && 0 === validate_file($cinfo['name'])) { $total_byte_count += $cinfo['size']; if ($total_byte_count < $use_memory) { $cache_indexes[] = $cache_index; $content_cache_highest = $cache_index; } } $cache_index++; } if (!empty($cache_indexes)) { $content_cache = $z->updraftplus_getFromIndexBulk($cache_indexes); } } $contents = isset($content_cache[$i]) ? $content_cache[$i] : $z->getFromIndex($i); } if (false === $contents && ('pclzip' !== $method || 0 !== $info['size'])) { return new WP_Error('extract_failed_'.$method, __('Could not extract file from archive.').' '.$z->last_error, json_encode($info));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!$wp_filesystem->put_contents($to . $info['name'], $contents, FS_CHMOD_FILE)) { return new WP_Error('copy_failed_'.$method, __('Could not copy file.'), $info['name']);// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!empty($info['size'])) $size_written += $info['size']; do_action('updraftplus_unzip_file_unzipped', $file, $i, $info, $size_written, $num_files); } $z->close(); return true; } } David Richards, Author at Smart Office - Page 41 of 91

    Smart Office

    Optus Set To Be Pushed To #3 Slot After TPG Gets Voters Nod To Takeover iiNet

    TPG is set to become Australia’s second largest ISP after Telstra after iiNet shareholders have approved a $1.56 billion takeover by the Sydney based Company.

    At a shareholder meeting in Perth this morning the TPG

    takeover offer won the votes, with 100.6 million in favour and 5.2 million

    against, 93% per cent of proxies were in favour of the deal.

    The deal went ahead despite several people voicing their

    objection to the proposed takeover which will see Optus relegated into the #3

    slot in Australia.

    TPG will now become a telecommunications powerhouse with 1.7

    million broadband subscribers and the power to reshape the Australian internet

    market.

    This places it behind Telstra’s 3 million accounts and ahead

    of Singtel-Optus’ 1.03 million users with M2 Group a distant fourth.

    Objections to the takeover came from Merlon Capital’s Hamish

    Carlisle and iiNet’s founder Michael Malone along with several major

    institutional shareholders including BT Investment Management, the3 original

    offer was based on $1.4 billion all-cash bid that was revealed back in March.

    The Australian Competition and Consumer Commission who is

    still investigating the deal is set to deliver their verdict on August 20th

    with insiders tipping a green light for the deal

    Fairfax Media said that once approval is given both parties

    will then immediately call for a court hearing to ratify the merger and

    finalise the deal.

     IiNet chair Michael

    Smith had warned that any move to reject the deal would most likely result in

    the company’s share price collapsing.

    The Australian newspaper said that the Australian

    Competition & Consumer Commission has been studying the impact the deal

    could have on the competitive landscape of the $40 billion a year telecoms

    sector.

    ACCC chairman Rod Sims said he was aware of the criticisms

    about the proposed deal from some segments of the telco market, but he said the

    ACCC would not rush into handing down its decision before its scheduled release

    date.

    “We are cognisant of commercial pressures out there, but

    this is a big deal and it will permanently change the landscape so we have to

    fulfil our duties and assess it probably,” Mr Sims said.

    Banned “Dell Sucks” Advertising Revealed

    SmartOffice News has obtained copies of a banned advertising campain developed by Sun Microsystems that claims that “Dell Sucks”.

    A controversial advertising campaign that has already been banned by the Wall Street Journal in the USA could soon be running in Australia. Developed by Sun Microsystems for the launch of the Sun Fire X2100, X4100, and X4200 64 bit X86 servers one of the proposed advertisements claims that “Benchmarks Show That Dell Sucks”.

    Paul O Connor the Marketing Manager for Sun Microsystems in Australia said, “We are not launching these servers until the end of the month however we are considering using the US creative in Australia. It will be interesting to see if they will get a run”. In the US a Sun Microsystems executive said “business publications like the Wall Street Journal have refused to run our bold ad concepts because the headlines were thought too controversial. At Sun, we’re the radical engineers that build “ass-whoopin” technology – we’re not Miss Manners and we never want to be. We ask all you contrarians out there to e-mail us your own provocative ad headlines: my-headline@sun.com.”

    Sun is claiming a technology world record for the new range of servers.  They claim that following testing by the Standard Performance Evaluation Corporation (SPEC) a non-profit corporation formed to establish, maintain benchmarks the Sun Fire X2100 server has set a new world record based on the on SPECjbb2005 benchmark, which is a follow on to the popular SPEC JBB2000 suite. The Sun Fire X2100 server posted single processor world-record result of 16,070 business operations per second (bops) using Solaris 10 operating system (OS). Since the benchmark stresses the implementation of the Java Virtual Machine, as well as the performance of the OS, it was used to demonstrate that Java Hotspot Server Virtual Machine can deliver outstanding results and optimised performance regardless of the underlying OS.

    Identical server configuration was used to conduct two more SPECjbb2005 tests using the same version of the virtual machine. The first test was run under MS Windows 2003 Server OS and produced the score of 16,053 bops and the second test used SuSe Linux Enterprise Server 9 OS and got the score of 15,434 bops. Java HotSpot once again proved to be a stellar performer, when used in combination with top-performing x64 servers from Sun.


    Click to enlarge


    Click to enlarge

     


    Click to enlarge

    Click to enlarge

     

     

     

     

     

     

     

     

    HP A “Basket Case” 30,000 Jobs Slashed As PC Sales Slump

    Hewlett-Packard who has been described as a” technology basket case” that has lost direction, is set to slash another 25,000 to 30,000 jobs in an effort to cut costs.

     

    In Australia HP has been bleeding losses, as they struggle

    to hold onto contracts, globally their PC division is also suffering, when

    Lenovo launched back in November the Company reacted to the the new consumer

    market entrant by slashing the price of their PC’s.

     

    A major supplier to the Commonwealth Bank whose systems

    crashed last week leaving millions without access to credit cards or able to

    access their accounts the Company will shortly spit into two divisions.

     

    About 10 percent of the jobs at the current HP, will be

    eliminated, company officials said early this morning.

     

    A year ago, Meg Whitman the CEO of HP who was in Australia

    recently to meet with Commonwealth Bank executives announced she was cutting

    Hewlett-Packard in two. This morning, she detailed job cuts expected at the

    company.

     

    “We’re looking forward to operating as two industry-leading

    companies,” said Ms. Whitman, HP’s chief executive, speaking at a meeting of

    financial analysts. “You’ll see us doing more pruning of businesses that don’t

    fit.”

     

    Ms. Whitman became the head of HP in 2011. As part of a

    restructuring announced in 2012, 54,000 jobs have been cut at the company. The

    new cuts are on top of that.

     

    In November, Ms. Whitman will become the chief executive of

    HP Enterprise, or HPE, which will sell things like computer servers, data

    storage, software and services to business.

     

    The other company, called HP Inc., will focus on printers

    and personal computers. Ms. Whitman has said the division will enable both

    businesses to react faster to changing markets.

     

    The expected job cuts will result in a charge of about

    $US2.7 billion, beginning in the fourth quarter.

     

    “We’ve done a significant amount of work over the past

    few years to take costs out and simplify processes and these final actions will

    eliminate the need for any future corporate restructuring,” Chief

    Executive Meg Whitman said.

     

    The total job cuts planned by the company as part of

    Whitman’s multi-year restructuring plan was 55,000 as of October last year. HP

    had more than 300,000 employees as of Oct. 31, 2014.

     

    In the latest third quarter HP’s revenue from personal

    computer and printer businesses, its largest, fell 11.5 percent. Enterprise services

    division sales dropped 11 percent, while revenue at the enterprise group rose 2

    percent.

     

    Hewlett Packard Enterprise is expected to have more than

    $US50 billion in annual revenue and report adjusted profit of $US1.85 to

    $US1.95 per share in 2016, HP said on Tuesday.

     

    The business is expected to report free cash flow of $US2.0

    billion to $US2.2 billion in 2016, at least half of which is expected to be

    returned through dividends and share buybacks.

    Aldi Starting To Hurt Appliance Retailers Bigger Stores Planned

    Every week Aldi is shifting tens of thousands of appliances and consumer electronic goods, sales that are being stripped from the likes of The Good Guy’s, Bing Lee, Betta Electrical and smaller appliance retailers say industry executives.

    The emergence of Aldi as a retail powerhouse is not only having an impact on Coles and Woolworths but appliance and CE retailers, distributors of appliances and brands such as Sunbeam, Breville and Delonghi said one senior executive. 

    Also impacted are distributors who for decades have supplied the mass retailers with branded and house brand appliances.

    Peter Hammerman the CEO of Seconds World said both distributors and retailers are under pressure from Aldi, he said that the quality of their house brand appliances is “terrific” and I know that their presence it is being felt in the industry” he said.

    He added “What we find is that in the weeks that Aldi roll out consumer electronics and appliance specials sales drop”.

    A senior manager at The Good Guys store in NSW said “What is worrying is that consumers are buying from Aldi stores and finding that the quality is excellent, they then go and tell their teenage children who have moved out of home or friends and they in turn are shopping at Aldi”. 

    “What is coming from Aldi is another 100 plus stores in WA and South Australia, they are also expanding their presence in the Eastern States, the bigger they get the more appliances and consumer electronic goods they will sell and this will hurt a lot of retailers especially smaller retail chains”.  

    Hammerman said “The quality of appliances coming out of China overall is very good, branded products from the likes of Sunbeam and Breville are made in the same factory as the Aldi appliances. I recently took a Breville and a Chinese Tiffany sandwich product home, they both delivered the same functions, the Breville product was $79 and the Tiffany $49, and ironically the Tiffany product was better”. He said.

    A Harvey Norman franchisee attending the Harvey Norman Conference in Melbourne said “There is no doubt that Aldi is having an impact. The downside is that the Company is set to significantly expand their stores across Australia and this will cause further problems for appliance manufacturers”. 
    “Since Aldi came into the market we have expanded our range of house brand appliances and they are selling well however we have not in some case been able to match Aldi prices”. 

    The big winners are not only Aldi but distributors such as Tempo and Euro Centra which is owned by German Company Wunsche Group. 

    This week Aldi is offering a 50″ TV from Temp for $529 with over 15,000 set to sell this week.

    They are also offering a bread maker for $79 and a rotisserie oven for $79.99. A Stirling dishwasher from Tempo is selling for $299, a similar featured product from The Good Guys is selling for $699. 

    Citigroup analyst Craig Woolford said that one area of concern is the floor size of Aldi stores. 

    Currently the average is 1400 square metres which he said restricted the volume or size of appliance products that Aldi could range. 

    He said that Aldi is currently trialling new concept stores in Victoria and New South Wales that were significantly larger than their current stores.

    New information supplied to a Senate Inquiry into tax payments in Australia reveals that Aldi Australia makes more profit on each dollar of sales than its much larger rival Coles, Woolworths, Masters and BigW and has more scope to cut prices in the event of a prolonged price war with mass retailers. 

    According to detailed accounts provided by Aldi to the Senate tax avoidance inquiry, the discounter earned a pre-tax profit margin of 5.2 per cent on sales of $5 billion in 2013.

    Brokers believe Aldi’s healthy profit margins provide the company with scope for further growth and room to cut prices amid escalating price competition.

    “The financials for Aldi confirm both its success and its room for further growth in Australia,” said Woolford, who expects Aldi to continue to gain market share from Woolworths and Coles, placing increasing pressure on their margins.

    “These numbers suggest Aldi’s operating margins in Australia are very healthy compared with the rest of the world and may be boosted by significant sales of high margin general merchandise,” Deutsche Bank analyst Michael Simotas told Fairfax Media. 
    “This provides the group with headroom for price investment if needed,” he said. “However, given the momentum, we do not believe it is needed at this stage.”

    Aldi’s submission suggests that it paid $83 million income tax on pre-tax profits of $261 million in 2013 – an income tax rate of 31.8 per cent and $67 million in income tax on earnings of $232 million in 2012  –  a tax rate of 28.8 per cent.

    About 1 per cent of its merchandise and services expenditure is made to international related parties, the company said.

    Big New 3TB Drive Coming From Seagate

    24 hours after we exclusively revealed in Australia, the launch of a complete new portable storage system from Seagate comes news that Seagate is set to launch a 3TB Constellation ES disk drive.

    Ideal for organisations serving video content the drives are primarily designed for heavy duty use.

    Originally revealed on the Register the enterprise-class drive would have a 6GBps SAS interface and have the largest capacity from any of its kind in the Seagate’s range. The largest current Constellation ES is a 2TB, 7,200RPM unit.

    A 1TB version of a 2.5-inch Constellation ES drive is also expected mid-year, and become the highest-capacity 2.5-inch drive. Also rumoured is a new Savvio 2.5-inch drive that would eclipse the current 600GB, 10,000RPM unit, likely upping capacity to 750GB.

    SAS, or Serial Attached SCSI drives, are used heavily in servers.

     

     

    HP Takes Top Storage Spot

    More than 5720 Terabytes of storage was shipped in the second quarter of 2005. This is up from 4,600 for the same period last year.

    On the eve of the opening of the Storage Networking World expo in Sydney, Gartner says external disk storage revenue in Australia totalled A$97.8 million in the second quarter of 2005.

    The amount of external disk storage capacity shipped in the same period was an “impressive” 5725 terabytes, Gartner said – up from 4600 terabytes in the first six months of 2004. The top three vendors in Australia for Q2 were Hewlett-Packard with a vendor revenue market share of 28.8pc, EMC with 18.1 and IBM with 17.9. For Hewlett-Packard alone this mean revenue of
    A$28 million, according to Gartner.

    “As more e-mail, video, graphic and audio information is created, organisations’ appetites for disk storage will continue to grow,”
    said Phil Sargeant, Gartner’s storage VP. “The challenge for organisations will be to digest this volume of storage. Good management techniques and good people will be required.” Storage Networking World opens at Sydney’s Darling Harbour complex today, with security exert and author Ira Winkler discussing Secrets Of Super Spies – The Truth Behind The Most Devastating Computer And Information Crimes. Larry Krantz of the Storage Networking Industry Association delivers a keynote at 9.50am. At 10.30 there’s a storage roundtable discussion with SNIA board members, conference presenters and end users including reps of Griffith University, Baycorp Advantage and Qld Uni of Technology. Around 40 storage companies will be showing their wares at the accompanying exhibition. They include Sun, H-P, HDS, IBM, Imation, Quantum, Sony, Falconstor, McData and Bakbone.

    According to IT research group IDC the worldwide external disk storage market was worth $3.8 billion  in Q2, 2005. This was up 8.6%.The total disk market was up 9.9% to $5.6 billion. IDC says: “Capacity continues to outpace overall revenue growth with total disk storage systems petabytes growing 59.3% year over year to 457 petabytes for the second quarter.”
    Sales of servers with 3 or more disk drives is driving internal storage sales, said Brad Nisbet, program manager in IDC’s Storage Systems program.
    EMC leads the external disk storage market with 21.2% revenue share, followed by HP and IBM with 18.8% and 13.8% revenue share, respectively. Dell edged out Hitachi for the fourth position with 8.3% revenue share while Hitachi ended the quarter with 7.3%.
    The total network storage market (NAS combined with Open and iSCSI SAN) posted 16.1% year-over-year growth in the second quarter to nearly $2.5 billion. EMC continues to maintain its leadership in the total network storage market with 27.9% revenue share, followed by HP with 21.3%. Dell and IBM posted the strongest year-over-year revenue growth for the quarter among the top 5 vendors, with 33.2% and 22.9% growth, respectively.
    The Open/iSCSI SAN market grew 17.8% year over year, surpassing $2.0 billion in revenues for the first time. Unlike the first quarter of 2005, there was no clear leader: EMC and HP were in a statistical tie for the first place position. EMC had 25.0% revenue share and HP had 24.8% revenue share for the second quarter. Dell and IBM posted the strongest revenue share gain among the top 5 vendors, with 1.6 and 0.6 year-over-year share point gain, respectively.
    In the NAS market, which grew 9.5% year over year, EMC led with 40.2% revenue share, followed by Network Appliance with 35.2% share. The iSCSI SAN market posted nearly 140% revenue growth year-over-year. Network Appliance continues to lead the market with 41.6% share, followed by EMC with 26.0% share.
    But then again Dow Jones newswires reports that “Hewlett-Packard Outgrows Market, Continues To Rank No. 1 In Worldwide Total Disk Storage Systems Rev” so there is some solace for everyone

    Dick Smith Sales Up 7.5%

    Days after announcing a move into appliances Dick Smith has announced that sales grew 7.5% in the last financial year,from $1,319.7m in 2015 vs 2014 $1,227.6m. Comp sales only grew 1% while EBITDA profits were up 7.4% to $79.8M.

    Dick Smith CEO Nick Aboud said that sales had been impacted by an intentional reduction in unsupported promotions, patchy trading conditions and one less trading day in the important ‘tax-time’ June sales period.

    The Company like JB Hi Fi is still struggling in New Zealand with Aboud claiming that Australia continues to stand out, achieving 21.9% EBITDA growth for the year and 30.1% EBITDA growth in the second half, resulting in an Australian EBIT margin of 5.5% for the year.”

    New Zealand’s gross margin was 23.5% of sales, reflecting a more competitive market and increased promotional activity, particularly in 1H 2015.

    Aboud said that the cost of doing business had decreasing 32bp to 18.7% of sales in 2015. 

    Abboud said “We are pleased to have delivered another solid underlying profit performance in this our second year as a listed company”.
     
    The Directors have declared a fully franked 5.0 cents per share final dividend, to be paid on 30 September 2015. Total dividends declared in 2015 of 12 cents per share, represents approximately 65% of 2015 NPAT before restructuring costs, which is consistent with the Board’s guidance of a 60-70% payout range.

    Dick Smith icreased stock levels of private label products which now represent 12.5% of sales, the Company said that they are looking to achieve 15% of all sales as house branded products.

    The Company also said that they now have nearly 2,000,000 members of their Mates Rates Club. 

    Click to enlarge.Dick Smith now has 393 stores however this could be impacted by an exit from David Jones Stores.



    More to follow.

    JB Hi Fi Expands Board With Addition Of Experienced Retail Executive

    The CEO of JB Hi Fi Richard Murray has announced an expanded board for the mass retailer who last week announced record profits and increased sales.

    The new board appointee is former Just Group operations manager and Peter Alexander sleepwear business chief executive Wai Tang. 

    Last week JB Hi-Fi reported a net profit of nearly $137 million for the 12 months to the end of June, up more than 6.3 per cent on a year ago.

    Sales grew by 4.8 per cent to $3.65 billion, with both figures exceeding analyst expectations.

    Comparable sales, removing the impact of the five new JB Hi-Fi stores that opened during the year, were up 2.9 per cent.

    Ms Tang who brings a wealth of expertise to the JB Hi Fi board was most recently general manager of business development for Pacific Brands and a current director of property group Federation Centres — will begin the non-executive role on September 14.


    New JB Hi Fi board appointee Ms Tang


    Mr King, who was chairman between May 2006 and September 2007, will retire after the annual general meeting on October 29, the company said in a statement to the ASX. 

    The board has appointed Ms Tang to the board as an additional appointee ahead of the annual meeting, when she will stand for election. 

    JB Hi-Fi chairman Greg Richards said Ms Tang would bring “a wealth of retail industry experience at both a senior executive and board level.

    Ms Tang was also the founder of the Happy Lab gourmet confectionary business and is also a non-executive director of Kikki K and the Melbourne Festival. 

    Her past board positions include non-executive directorships with Specialty Fashion Group and the Melbourne Fashion Festival.

    Mr Richards said Mr King had made a “significant contribution” to the business over more than a decade.

    “His experience, expertise and advice have been of great value to the business as it has gone from strength to strength over the past 11 years,” he said.

     JB Hi-Fi said last week that they are is expecting even stronger sales growth in the coming year.

    “We are pleased with our start to the financial year,” said chief executive Richard Murray.

    “July trading has been solid … Key growth categories included computers, telco and home appliances.”

    The directors did not comment about the entry of Dick Smith into the appliance market however they did say that JB Hi Fi Home is where the sales growth is going to come from in the future. 

    Another four JB Hi-Fi Home stores opened last year, taking the total number of locations dedicated to white goods to 43.

    The company says it will also be adding small household appliances to 14 of its traditional stores by November.

    “We see our continued expansion into home appliances and, ultimately, the connected home as a significant opportunity for JB in the future,” said Mr Murray.

    The proportion of sales online also continued to grow, from 2.2 per cent of total sales in financial year 2014 to 2.4 per cent in financial year 2015.

    That reflected online sales growth of nearly 17 per cent in the past year.

    EMC Moves Further Into SMB Market

    EMC have finally consolidated a hardware software solution for SMB organisations following the purchase last year of Dantz.

    EMC have introduced a new line of Insignia storage hardware and software that enable small and medium businesses of up to 20 employees to store, manage, protect, and share business information.

    The EMC Insignia products are designed specifically to address SMB’s needs spanning critical information management and storage issues. Low cost the EMC Insignia products have beeneveloped to work individually, together, and with third-party software and hardware.

     In October 2004, EMC acquired Dantz Development who developed the Retrospect backup and recovery software. Since then the Company has combined the extensive SMB market knowledge acquired through Dantz with EMC’s expertise in information management and storage to create the Insignia product line.

    Joe Tucci, Chairman, President, and CEO of EMC, said, “Small and medium businesses are faced with the same challenges as our enterprise customers – explosive information growth, the need to improve productivity and lower costs, and a requirement for simplicity. EMC Insignia brings EMC expertise in these areas to a new and adjacent market at appropriate price points. Our new solution enable SMBs to focus on growing their businesses rather than expending unnecessary effort to store, manage, protect, and share their information.”

    Larry Zulch, Vice President and General Manager, EMC Insignia, and former CEO and co-founder of Dantz Development  will manage the EMC Insignia business. The EMC Insignia line includes the following new hardware and software products:

    EMC CLARiiON AX Series disk arrays enable SMBs to consolidate and share storage efficiently among multiple computers and capitalise on the advantages of backup to disk. The combination of EMC networked storage for fibre channel or iSCSI delivers easy-to-use, highly reliable data storage at an affordable price.

    EMC Storage Administrator for Exchange SMB Edition information management software works with the EMC CLARiiON AX series to simplify management of Microsoft Exchange Server data, provide fast recovery from an Exchange server failure, and make migration to Exchange Server 2003 a straightforward and streamlined process achievable in just a few hours in many cases.

    EMC Retrospect gives SMBs a simple but powerful way to protect servers, 24×7 applications, desktops, and notebooks. The backup and recovery software is renowned for its ease of use, self-adjusting backup operations, accurate point-in-time restores, simplified onsite and offsite media management, and encryption of backup media with government-certified AES.

    EMC RepliStor SMB Edition guards SMB’s vital information against hardware failure or site-wide disaster. The software provides easy-to-manage replication of data between two Windows computers locally or remotely across the Internet and maintains a continuously updated copy.

    EMC VisualSRM SMB Edition helps businesses stay ahead of information growth and rein in costs while meeting storage needs. This storage resource management software presents a consolidated view of a company’s storage and hosts automated policies to manage it.

    EMC eRoom SMB Edition brings efficiency to how employees, partners, and suppliers work together. The collaboration software provides a browser-accessed, highly secure collaborative workspace to share information, manage projects, and deliver higher-quality products and services.

    “We’re seeing the IT needs of small and medium businesses mirroring those of large enterprises, but on a different scale,” said Ray Boggs, VP of SMB Research at IDC. “SMBs just don’t have the financial and IT resources to deploy the same comprehensive approaches to storage. Small and mid-size firms definitely prefer suppliers that can provide comprehensive solutions, but they also like the option of adding capabilities a la carte as needed. The EMC Insignia line of hardware and software products offers SMBs the flexibility of a complete portfolio of capabilities that work together and can be put into place when the time is right.”

    Pricing and Availability
    The CLARiiON AX100/i, Storage Administrator for Exchange SMB Edition, and Retrospect are available immediately from authorised EMC resellers and EMC Velocity SMB channel partners. RepliStor SMB Edition, VisualSRM SMB Edition, and eRoom SMB Edition will be available in Q1 ’06. Pricing for the CLARiiON AX100/i starts at AU$8289 / NZ$9028, Storage Administrator for Exchange SMB Edition costs AU$3006 / NZ$3274, RepliStor SMB Edition costs AU$1500 / NZ$1634 per node, VisualSRM SMB Edition starts at AU$1500 / NZ$1634, eRoom SMB Edition starts at AU$1500 / NZ$1634 for 10 users, and Retrospect starts at AU$601 / NZ$655. Available in ANZ from April 1, 2006.

    LG Scarlet TV Launch More C Grade Than A grade

    COMMENT: There are A grade celebrity launches and C grade celebrity launches but the event held last night in Sydney for the new LG Scarlet LCD TV was definitely a C grade launch attended by B grade celebrities and those paid to attend which included actress Natassia Malthe who is best known for her appearances in B grade movie flicks such as Sex & Death, Blood Suckers, and Alone in The Dark.
    Also making a quick appearance were members of the Sharks NRL football team who just happen to be sponsored by LG the makers of the new Scarlet LCD TV and a host of reality TV rejects who appeared quite happy sipping warm pink champagne.

    Click to enlarge

    Click to enlarge
    Unknown models parade on a catwalk


    In a desperate attempt to reverse their declining position in the LCD TV market LG, under the direction of Warren Kim the former national merchandising manager for Telstra’s Digital, Data, IT and Fixed Line business the Korean Company is spending over $100 million dollars in an attempt to take market share away from Sony, Samsung and Panasonic in the flat screen TV market.

    LG’s strategy however is flawed. Rather than be evolutionary like Sony Samsung and Panasonic LG has chosen to be revolutionary. Their strategy is wrapped around the launch of a bright red backed LCD TV called Scarlet.

    Talk to any automotive marketing manager and they will tell you that red cars do not sell and even worse is their resale value.  But to convince consumers to buy a red backed TV which in 2 years time will look so dated is a massive risk.

     

    In an attempt to jack up interest in their new Scarlet TV LG in their wise wisdom decided on a global tease campaign  the company made readers and TV viewers believe that a new movie starring Norwegian model and actress Natassia Scarlet Malthe and produced by David Nutter ? of the hit show “The Sopranos” ? was in the making.

    From billboards and bus shelter posters to online banners, Malthe has made public appearances at film premiers around the world including last night in Sydney in support of her fictitious role, while the ad copy offered no hint of LG. Also in toe was Director David Nutter who between them they are believed to be netting in excess of $15M dollars.

    Click to enlarge
    Graham Cunningham LG sales Spruikes Scarlet TV

    Click to enlarge
    LG sales boss spruiks Scarlet


    Not making a show at the local Scarlet TV launch was Aussie actors Gary Sweet or former Home and Away star Sharni Vinson who appear in the tease trailer alongside Natassia Scarlet Malthe. Also missing was recently appointed LG Marketing Director David Brand who gave the event a pass in favour of LG marketing events in New York and Paris.

    “To spend money without letting people know about our brand is really risky. But that’s the punch line of this campaign. It will change the rules of the game,” Lee Kwan-sup, vice president of LG’s Digital Display Global Brand Marketing Team, said, adding his company can’t match its bigger rivals on media spending.

    “What we really want is to increase our brand recognition. Strengthening consumers’ emotional attachment to that brand is the key to long-term success,” Lee said.
     


    The only problem is that the new Scarlet LCD TV is very ordinary up against some tough new offerings from Samsung, Panasonic and Sony the current market leader in the TV market.

    Rather than be evolutionary LG is banking on being revolutionary and like the big budget tease flick the TV lacks any substance above and beyond what LG has offered in LCD TV’s in the past. This is the same Company that 3 years ago rolled out the Internet fridge which at the time they said would be a big hit. Today I cannot find one retailer stocking the fridge let alone a consumer owning one.