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 48 of 91

    Smart Office

    Harvey Norman To Cut Back On IT & Consumer Electronics

    Harvey Norman is set to move away from relying on consumer electronics and IT to drive sales; a move which analysts claim could benefit JB Hi Fi, The Good Guys and Betta Electrical.
    Instead the national retailer is set to expand their homeware and small appliances range after Harvey Norman Chairman Gerry Harvey warned of a 40% plunge in profits.

    Several IT and CE vendors polled by ChannelNews claim the move could have an impact on their overall sales. Some vendors claim the move could benefit JB Hi Fi and online resellers of technology goods.

    “The results are worse than we expected,” chairman and co-founder Gerry Harvey said yesterday.

    An adjustment in property values has also impacted the retailer who said that the value of the retail stores they own would be down 29.5 per cent to $252.6m.

    Pre-tax profits would be down by about 20-25 per cent, after full-year sales fell 7 per cent to $5.74bn, compared to the previous year.

    Like-for-like sales also fell 6.5 per cent.

    Franchisees at Harvey Norman have told ChannelNews that “negative” comments by Gerry Harvey in 2011 relating to online sales and the need for additional taxes on overseas sales had impacted the retailer with “hundreds of consumers switching their loyalty to other retailers”.

    Despite the poor result Harvey Norman shares closed half a cent higher at $1.975.

    Gerry Harvey believes price deflation played a major role in the Company’s sales decline; he said that TVs were $1000, less than half what they used to cost two to four years ago.

    “A Toshiba laptop costs $3500 five years ago. Now you can pick up an even better one for $500,” he said.

     

    “Price deflation in TVs and computers is happening across the world. It’s a big problem. Products are being sold for less than what it costs to produce them.”

    Platypus Asset Management chief investment officer Don Williams told the Australian newspaper it was not surprising that Harvey Norman was suffering in a tough retail market where electronic prices are falling.

    “The current price deflation is not going to go away soon,” Mr Williams said. “As a business, Harvey Norman has lost market share for some time to other retailers and online sales.”

    Retravision Doomed Say Vendors

    Retravision is doomed say several CE Vendors with Narta the consumer electronics and appliance buying group for David Jones, JB Hi Fi, Bing Lee and several other leading retailers set to be bolstered by the sale of Retrovision’s largest retailer RT Edwards of South Australia to Radio Rentals.

     

    Industry insiders say that he move to Narta who are responsible for buying over $ 3.3 billion dollars worth of goods will cripple Retravision and could well force the closure of other Retravision stores due to a lack of buying power.

    During the past 18 months Retravision has reeled from one crisis to another.   In 2006 Retravision NSW was forced into administration. Also in 2006 Narta acquired Sydney based Whitfords. Also joining the buying group that year was Coffs Harbour based Morrison Electrical who deserted Retravision after more than 40 years.

    Kay Spence the CEO of Narta told ChannelNews that she is currently in New Zealand and that while the Radio Rentals business will bolster Narta buying power, she was not in a position yet to comment on the issues affecting Retravision.

    Several vendors who ChannelNews have spoken to today have said that they believe Retravision is doomed. “The insurance Companies are worried and we for one are reviewing our terns” said a major TV vendor.

    An appliance vendor said “We don’t like kicking someone when they are down but the writing is on the wall. Narta are a very strong, well-run group and from here on in they look like getting stronger”.

    A senior JB Hi Fi executive said “he fact is that both here and around the world,  the big electronics retailers are getting bigger and the small are just falling by the wayside”….many years ago when we started in this business, Retravsion was bigger than Harvey Norman, but that situation has well and truly reversed itself….it does put a lot of pressure on Retravsion and their business model”

    Overall, looking at the sales figures, which i believe was quoted at around $100 million, it wont have a great impact- maybe it might for NARTA, but not for the overall sector..it sill have an insiginicant affect on the total market”.

    “As for JB HiFi, we really don’t compete with them, so it wont affect us at all”.

     

    More to follow.

    Corporate Express Sold New Office Works Competitor An Option

    Corporate Express one of the biggest buyers of PC and office supply gear in Australia has been sold to U.S. office supplies company Staples with speculation now mounting that the Company is considering the setting up of a retail office supply network in Australia to take on OfficeWorks.

    Corporate Express one of the biggest buyers of PC and office supply gear in Australia has been sold to U.S. office supplies company Staples with speculation now mounting that the Company is considering the setting up of a retail office supply network in Australia to take on OfficeWorks.

    A senior executive of Staples told ChannelNews that a retail chain in Australia is an option and that “once the Corpate Express deal has been bedded down” the concept of a retail chain in Australia will be considered.

    Staples has agreed to pay A$2.9 billion dollars for the Dutch owned Corporate Expresswhich has a major footprint in Australia.

    Staples on Wednesday said it had raised its all-cash bid to 9.25 euros per share from the 9.15 euros it offered last week. Its first offer, in February, was 7.25 euros per share.

    According to Reuters shares of Corporate Express rose 1.4 percent to 9.20 euros, almost triple the five-year low of 3.18 euros they touched in January before the takeover battle began. Staples shot up 4.4 percent, or $1.02, to $24.17 on Nasdaq.

    For Staples, the deal expands its presence in the company contracts business where it competes with Office Depot and OfficeMax as well as Corporate Express.

    “Staples will dwarf other office supply distributors,” Credit Suisse analyst Gary Balter said in a research note.

    “The acquisition will move Staples to a leadership position in the contract (large company) segment in North America and will create a solid base on which to build its global contract footprint, with a No. 2 position in Europe and a No. 1 position in Asia-Pacific,” Balter added.

    Technology & Appliance Advertisers Could Do Better

    Consumer technology and appliance advertisers who spend million on marketing using a media buying Company could get better performance if they focus on the people working on their account claims Richard Halmarick a former media agency boss.

     

    He claims that the experience of people working on technology accounts is often lacking because of staff churns as high as 70% within some media buying Companies.

    Halmarick the former head of Aegis Media who spent many years working on the LG account has established a new business called Kinessis in partnership with Allan Medford the former regional director of Universal Mcann and Alan Robertson a former part owner of Merchant & Partners.

    The primary objective of the business is to help advertisers get better value from their media buying Company.

    Halmarick claims that technology advertisers could do better if the people working on their account had a better understanding of technology. “There is a high degree of staff churn in media buying Companies and this could hurt as technology is a specialised area”.

    “Improvement is better than simply asking agencies to pitch and replace the incumbent” said Medford in an interview with the Financial Review.

    “We want to help clients get better performance as during a recession a lot of Companies place greater scrutiny on agency performance and media costs” said Halmarick.

    The Company that will only work for vendors believes that a lot of reviews are based on price rather than performance.

     

    Often media account reviews done by our competitors come down to the lowest common denominators that is, how low agencies can drop their fees and the ad buying rates agencies can extract from media Companies” said Medford.

    “Fees and rates are only a small part of the story. Clients need to get a lot more from their media buying Company” he said.

    In recent weeks ChannelNews has been told that at least three major advertisers are set to review their media buying Companies.

    Dick Smith To Be Closed Thousands To Lose Jobs, Prosecutions Tipped

    As exclusively tipped by ChannelNews Dick Smith receivers Ferrier Hodgson has moved to liquidate the mass retail group as interested parties walked away from the deal.

    The closure will affect 2460 staff in Australia, and 430 in New Zealand.

    According to sources the information provided by Ferrier Hodgson was riddled with uncertainties resulting in three powerful Asian bidders walking away from a deal. 

    According to a statement released by receivers James Stewart, Jim Sarantinos and Ryan Eagle of Ferrier Hodgson. 

    In Australia, 301 stores will close, and 62 stores will close in New Zealand.  

    Move stores were included in the closure announcement, and stores located in airports were excluded. 

    The fashion-focussed Move concept chain launched in 2013, and grew to a total of 12 stores nationally. 

    Mr Stewart said attempts to sell the Dick Smith group had been unsuccessful. 

    “While we received a significant number of expressions of interest from local and overseas parties, unfortunately the sale process has not resulted in an acceptable offers for the group as a whole or for Australia or New Zealand as standalone businesses,” he said. 

    Mr Stewart expressed his sympathies for employees, and said the outcome was disappointing for them. 

    “We would particularly like to thank the Dick Smith employees for their support and patience during the receivership process,” he said.

    Employees at head offices and stores have been briefed on the closures today, the statement advised.

    They will also be provided with “appropriate outplacement support”, it read. 

    All Australian workers are expected to be paid in full.

    “All Australian employee entitlements will rank as priority unsecured claims ahead of the secured creditors and are expected to be paid in full,” the statement read. 

    “Entitlements of New Zealand employees who are made redundant are preferential claims ranking ahead of the secured creditors, and are expected to be paid in full up to a maximum statutory limit of NZD$22,160 under New Zealand law.”

    ASIC Moves In On Dick Smith Directors While Other Retailers Eye Off The Carcass

    The Australian Securities and Investment Commission who have more powers, now that Dick Smith has been placed into liquidation, is set to move on former senior executives of the mass retailer with several former executives supplying key information on questionable business activities at the mass retailer.

    The big question now is what was not disclosed to shareholders that should have been disclosed. 


    Yesterday Ferrier Hodgson announced that they were set to terminate 2460 staff in Australia, and 430 in New Zealand, all stores in the network will be closed. 

    We can also reveal that one the main sticking points for potential bidders was the liabilities associates with the leases for Dick Smith stores as well as costs associated with the restocking of the stores. 

    Insiders have told ChannelNews that now that Dick Smith is set to be liquidated, several Dick Smith stores could be taken over on new leases’.

    Among the retailers interested in several Dick Smith stores in Australia is The Good Guys who made contact with receiver Ferrier Hodgson earlier this month.

    Channel News has also been told that some stores in New Zealand could also be sold separately along with the Move stores in Australia, we understand that 20 key sites in Australia have been targeted by interested parties. 

    One interested party is set to enter negotiations to buy the

    Dick Smith name and the online operation. 

      
    During the next few weeks’ receivers Ferrier Hodgson who has deliberately leaked information to select media in an effort to spin a positive story about Dick Smith is set to make millions from the collapse of the mass retailer while suppliers to Dick Smith are left with millions in losses.

    Some distributors could also be placed into liquidation.

    Both the National Australia Bank and HSBC are owed over $138 Million however there is little chance of the banks now recovering this money.

    Both Indian conglomerate Tata and Chinese consumer electronics retailer baulked at the risks associated with restocking the mass retailers as research showed that the Dick Smith brand was “seriously” damaged. 

    Currently ASIC is investigating the actions of directors. Several former employees have already supplied statement to ASIC investigators. They include former buyers and management who walked away from the Company claiming to ChannelNews that they were not prepared to “put up or be tarnished” with questionable business activities.     

    James Stewart from Ferrier Hodgson who has ducked away from any serious questions about the actions of Ferrier Hodgson staff running a so called receivers sale or the the actions of former directors of Dick Smith claims that the banks are facing massive losses. 

    We have been told that the Banks are already co-operating with ASIC and that information that has been uncovered by the receivers is currently being reviewed by investigators. 

    Joseph Hayes from McGrathNicol is believed to have already started a formal investigation into the collapse of Dick Smith which has left executives such as Phil Cave the Chairman of Anchorage Capital whose brand is seriously damaged in the investment community, is laughing all the way to the bank with over $300 Million that Anchorage netted from the float of Dick Smith. 

    Dick Smith the founder of the business claims that the business was only worth $90 million and the hype generated by Nick Aboud and Phil Cave was misleading.  

    Dick Smith has accused Anchorage Capital the Company that floated Dick Smith netting $520M of “destroying” the business and putting close to 3000 staff out of work.

    The founder of the original Dick Smith business said he had no interest in buying back the rights to the ‘Dick Smith’ name or any part of the troubled business.

    “I wouldn’t look at buying back the name but I’m incredibly angry about the utter dishonesty of Anchorage Capital and I hope ASIC and the Senate Inquiry do something about them,” Mr Smith said.

    Anchorage bought Dick Smith from Woolworths for about $94 million in 2012, before listing it through a $520 million public float 15 months later.


    Dick Smith’s inventory was written down by $60 million on December 1 last year, two months after chief executive Nick Abboud issued profit guidance for the 2016 fiscal year of net profit of $37 million to $43 million.

    Last week former CEO Nick Aboud did not comment when spotted at Balmoral Beach.

    Also ducking for cover is former Chairman Rob Murray.

    Hayes is the person acting in the interests of unsecured creditors such as distributors and vendors who have left millions out of pocket. Companies such as Roadhound who are owed over $18M and TV distributor Yale Prima who has told ChannelNews that they are owed over $10.2 Million dollars. 

    It is now clear that the unsecured creditors will get nothing.

    Then there is the inquiry by Senator Nick Xenophon’s whose inquiry into the collapse of “listed retailers” such as Dick Smith is set to go nowhere as it lacks any real powers other than being a means by which politicians can get media exposure in an election year. 

    The Financial Review said that Stewart is reluctant to give precise numbers in relation to Dick Smith’s current financial position except to say that the company has $200 million in inventory and its employees are owed about $30 million in entitlements.

    The employees have priority and will get what they are owed, Stewart says.

    Stewart will probably pay the suppliers cash to satisfy the retention agreements and then sell the inventory as part of the normal fire sale process.

    He has employed an international inventory specialist, Hilco Industrial, to assist him with the fire sale. Hilco is doing well out of Dick Smith. It was hired by the former owner Woolworths to value its inventory before it was sold to Anchorage Capital Partners in 2013.

    Anchorage paid about $94 million for the business in 2013 and then sold it to the share market at a valuation of $520 million about a year later.

    But Stewart followed the usual process of a receiver by running a liquidation scenario analysis. He found that the secured lenders would be better served by rejecting the purchase offer.

    Stewart said in a statement: “The offers were either significantly below liquidation values or highly conditional or both.”

    The sale process was affected by the company’s history. Discretionary retailers have been attractive to private equity which has turned them around and sold them to the market.

    Feds Do Nothing As Banks Hurt Small Business

    COMMENT: One has to seriously question whether the current Federal Government actually understands small medium business and that right now thousands of SMB organisations are starved of cash because of high interest rates and a refusal by banks to loan money to SMB organisations.

    During the past week I have spoken to several small medium business owners who are terrified that they are set to lose their business because banks are still charging up to 9% interest on overdrafts to small medium businesses while also refusing to loan them additional money that will help them manage the economic downturn.

    One has to seriously question whether the current Federal Government actually understands small medium business and that right now thousands of SMB organisations are starved of cash because of high interest rates and a refusal by banks to loan money to SMB organisations.


    Do they actually realise that the mainstream Banks in Australia are still charging up to 8 or 9% interest on business overdrafts  to SMB organisations of which there are more than 2 million small businesses in Australia employing approximately 4.5 million people.


    And if those businesses start falling over it will create a much bigger problem than whatever the decline in the automotive or construction industries will have on the economy.


    Small business in Australia has a total capitalised worth of $4.3 trillion 4 times that of the Australian stock exchange. Small business is a very important sector of the Australian economy.

     

     

    In the consumer electronics and IT industry the biggest suppliers are small medium businesses that distribute technology products into retailers who are responsible for 11% of all employment in Australia.


    The reserve bank has set the prime interest rate at 3.5% so in essence banks are pocketing between 4.5% and 5.5% on the money they lend to SMB organisations.


    They are also raking in billions from the 12 to 18% interest rate they charge on credit cards.


    But what is the Federal Government and Prime Minister Kevin Rudd and Tresurer Wayne Swann doing for SMB organisations. We know that they are concerned about the automotive industry which is primarily made up of foreign Companies who take their money out of Australia and we know that he is concerned about the mining industry who also exports their profits.


    But what about SMB organisations that keep the bulk of their profits in Australia and are the engine room for the economy.
    Surely there are grounds to hold an inquiry into the high cost of money for the SMB industry and surely the industry is worth supporting not by pouring in money to prop up organisations but by creating a fair and level playing field.

     

    Why should the Government pour money into construction and automotive companies like James Hardie and Brookfield Multiplex, who are going to take their profits out of Australia after being propped up by Kevin Rudd?


    And what’s to stop the Australian Government tipping $10B into a fund strictly for SMB borrowing where the interest rate is 1.5% above prime. The answer is nothing.

    Late last week the governor of the Reserve Bank, Glenn Stevens, said at a Senate hearing that bank chiefs should not let overzealous loan officers choke credit to small businesses and increase the risk of recession.

    Mr Stevens told the Senate hearing that he Reserve was ready to lower interest rates further if it was needed however there is every chance that SMB organisations will not see lower interest rates due to banks interest gouging. So while home loan mortgage rates fall to 5.2% and lower the chances are that SMB organisations will over coming months start laying employees off and then there will be a bigger issue as to where the money is going to come from to pay the low mortgage payments.

    Is Big W & Masters Set To Flogged Just Like Dick Smith?

    Woolworths who are struggling with their Masters and Big W stores could consider flogging the two retail groups in the same way that they offloaded their Dick Smith assets claim analysts.

    Back in 2012 Anchorage Capital acquired the Dick Smith assets from Woolworths for an initial $20M and a later payment of $74M, 18 months later the consumer electronics retailer was floated for $344M. The Company was not available to comment on the latest rumour that two more Woolworths assets could go up for sale. 
     
    Global buyout firm Kohlberg Kravis Roberts is believed to be circling the struggling Woolworths operation who recently reported a 12.5% slump in profits. 

    Several private equity Companies have told ChannelNews that they would be interested in buying the Big W and Masters chains who recently expanded their consumer electronics and appliance offerings.

    Last week Woolworths appointed Gordon Cairns as Woolworths’ new chairman.

    Shortly after his appointment the former Lion Nathan boss and current Origin Energy chairman signalled that all aspects of the business would come under review, he did not rule out selling Woolworths assets. 

    While Woolworths has never been a seller, Mr Cairns’ comments after his appointment on Friday would have offered acquisitive buyout firms some encouragement.

    After being appointed chairman, Mr Cairns, a veteran consumer goods executive, told The Australian he had “an open mind” about the future of the Masters hardware store chain and would make a decision based on the facts once he was in the job.

    “Given that the facts look so disadvantageous for Masters, we see this comment in a very positive light,” Bank of America Merrill Lynch analyst David Errington, in his research commentary.

    Publicity Seeking Choice, Now Has A Go At Netflix

    Choice the Australian consumer advocacy group who has turned to publicity stunts to drum up interest in their organisation, has taken a pot shot at Netflix over their geo blocking plans.

    Choice basically wants Australians to breach copyright laws in an effort to overcome Netflix’s attempts to stop the illegal downloading of content that Netflix does not have the rights to in Australia.

    It was only a few months ago that Netflix was crushing Samsung washing machines in an effort to generate publicity. 

    Right now Netflix does not have the rights in Australia to certain Hollywood content. These rights are held by organisations such as Foxtel, The Seven and Ten Networks and Nine Entertainment or Roadshow who have paid large sums of money for the rights to this content. 

    Netflix who are moving to become a global content Company have said that they will respect the rights of Copyright holds. Choice has said bugger this we will urge Australians to use questionable practises to obtain the content.   

    Last week Netflix announced that it will use technology to halt proxy tools that allow subscribers watch programs available in other countries but not in their own.

    Choice has slammed the decision, pointing out that there are about 8,500 items in the U.S. Netflix library compared to 1,300 in Australia.

    Choice has not commented on the concept of people using technology to unlock them 

    Now Choice whose subscriptions are falling, is urging Australian subscribers to help each other to continue using ‘unblockers’.

    ‘Many Australian Netflix subscribers will be shocked to find that they’ll be downgraded from accessing U.S. Netflix to the much smaller Australian library – losing out on thousands of titles,’ Choice director of Campaigns and Communications Matt Levey said.

    ‘Up until now, Australians could shop internationally for content using a simple unblocking service and their Australian account to access Netflix international catalogues.’
    ‘The popularity of Netflix in Australia has a lot to do with its progressive approach to content that allowed consumers to access more of the latest release programs from around the world in a timely manner.’

    Choice argued that the 340,000 Australian subscribers were ‘baited and switched’ by the U.S. streaming company.

    ‘Rather than putting barriers up, it’s time to recognise Internet as global”. 

    Netflix’s Vice President of Content Delivery Architecture David Fullagar told ChannelNews at CES that Netflix is working to allow all 190 countries where it is available to view the same library “but it won’t happen just yet” he said. 

    ‘We are making progress in licensing content across the world and, as of last week, now offer the Netflix service in 190 countries, but we have a way to go before we can offer people the same films and TV series everywhere,’ he wrote.

    Linksys Find The Hot Spot

    Occasionally there are gadgets that come along that really hit the mobile hot spot. One of those is the new Linksys Wireless-G USB Network Adapter and Wi-Fi Finder (WUSBF54G).

    I have just got back from a quick 10 day trip to Europe visiting Germany, Spain and the UK and just before I left the office a Linksys WiFi finder that doubles as a network adapter arrived for review so I decided to take it along. The new Linksys Wireless-G USB Network Adapter and Wi-Fi Finder (WUSBF54G) is a pocket-sized device that combines an easy-to-use wireless network scanner with a USB-connected Wireless-G network adapter.

    In a nutshell this product is great and I will not travel without one again. Let me tell you why. Firstly Europe is miles ahead of Australia in delivering wireless networks in public places. At every hotel that I stayed at including two in Majorca and two in the UK I was able to use the Linksys WiFi finder to find a free wireless network without having to open up my notebook or pay for the network access.

    Where Australian hotels are charging up to $19.00 a day to access broadband European hotels including on in the middle of the Island of Majorca are giving broadband away for free to hotel guests. However when moving between airport and train station getting the laptop fired up just to see if you can find some WiFi hotspot to leech off is a pain in the backside and that’s where the latest gadget from Linksys comes into play as I was able to find several free networks including one while sitting on a UK train.

    Some WiFi finders are bulky, some find a signal and don’t tell you if its a secured network or not and others are just WiFi finders and still require you to have a WiFi card for your laptop. Luckily the Linksys WUSBF54G is none of these starting with its small size, the Linksys WiFi finder is a bout the size of your average USB memory stick and still it manages to cram in a useable LCD screen.

    To use the Linksys WUSBF54G WiFi finder is simple. Firstly the kit is split into two parts, a scanner and a network adapter. A user takes out the scanner finds a network and then plugs the scanner into the Ethernet port on the notebook to access the network. A big plus is the ease with which one can ascertain the presence of a network with this device. In the past one had to boot a notebook wait for the Microsoft operating system to boot and then go to wireless setting to find a network using the Intel Centrino technology.

    In some cases this could take up to 5 minutes only to find that there is no wireless network present.

    In-Stat, a market research firm, reports that there will be almost 200,000 wireless hotspots available by the year 2009.

    A push of a button on the WUSBF54G initialises a search for 802.11 wireless networks reachable via its 802.11G network adapter. I was able to set the search to include only open networks, all networks, or to search for signal strength in various areas for a specific network name (SSID).

    Upon determining the networks in the area, the LCD screen on the WUSBF54G displays each network’s SSID, signal strength, 802.11 mode, and channel along with both the type and status of security. Its advanced search technology filters out 2.4GHz interference given off by microwave ovens, cordless phones, and Bluetooth devices, providing only information on 802.11 wireless networks. It then presents the networks in order of signal strength, until a specific network is selected.


    Once a network is determined by scrolling through the presented information, the WUSBF54G can be used to establish an 802.11G network connection via the USB port on a notebook that has installed the included client utility software. This I found great as I was easily able to ascertain which wireless network had the strongest signal. The only downside is the price in Australia $A139.00. In the US this product is $A112.00 and in Europe $A118.00.

    Linksys WUSBF54G WiFi finder reccomended retail: $139.00