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

    Smart Office

    Blockbuster To Get Sneek Peek At Circuit City

    According to the Washingtonn Post US consumer electronic retailer Circuit City will open its books to Video Ezy partner Blockbuster and billionaire Carl Icahn in a step toward a possible sale of the company.

    According to the Washingtonn Post US consumer electronic retailer Circuit City will open its books to Video Ezy partner sBlockbuster and billionaire Carl Icahn in a step toward a possible sale of the company.

    The mass retailer who has struggled up against Best Buy recently hired Goldman Sachs to review its potential strategies, but Circuit City’s board is not pursuing a particular option, whether a sale or otherwise, said chief executive Philip J. Schoonover said the Post.

    Click to enlarge

    In April, Blockbuster made an unsolicited cash bid to buy the struggling retailer for $6 to $8 per share, totaling as much as $1.35 billion, a deal that would have roughly doubled Blockbuster’s market value. Blockbuster envisioned merging the companies’ products to create multimedia and electronics stores. But Circuit City questioned whether the movie-rental chain could finance the deal.

    In a letter dated April 24 and filed with the Securities and Exchange Commission yesterday, Icahn, Blockbuster’s largest shareholder, said that he would purchase Circuit City if Blockbuster could not secure funding or shareholder approval. Circuit City had asked for the written commitment before opening its books.

    Schoonover cautioned against reading too much into the situation.

     

    “Let me be clear that our decision to allow Blockbuster and Carl Icahn to conduct due diligence should not be taken as an indication that the board has completed its review of the Blockbuster proposal, that the board has taken a position on the company’s value or that it has settled upon a particular strategic course of action,” he said in a statement.

    Circuit City’s shares jumped 28 cents, or 5.9 percent, to $5.07. Blockbuster’s stock fell 2 cents, to $2.66.

    Recently, Circuit City has been fighting a number of battles.

    During its previous fiscal year, which ended in February, it lost a record $319.9 million, which it blamed on the weakening economy. The company has been losing ground to Wal-Mart and Best Buy. And this year, it opened smaller, more intimate stores — dubbed The City — to halt its slide.

    As it struggled, Mark Wattles, an activist investor who owns 6.5 percent of the outstanding shares, engaged Circuit City’s executives and board members in a heated proxy battle. He sent a letter telling the company to replace Schoonover and put itself on the block. Earlier this year, Wattles, who co-founded Hollywood Video, called for an overthrow of the board and nominated five replacements.

    But yesterday the combatants reached a settlement. Circuit City agreed to include three of Wattles’s nominees in the slate of directors to be elected at the annual meeting in June. One of the nominees will become a member of the board’s executive committee.

    For more see the Washington Post

    Mobile Apple Phone Coming Soon

    Apple is set to launch a brand new mobile phone that will featuring music, video, email, and Internet functions. It will run on a unique Apple network called Mobile Me.

     Apple Computer has requested a set of trademarks for a mobile telephone service featuring music, video, email, and Internet functions, according to applications at the US Patent and Trade Office.

    The maker of the iPod music players filed this month to trademark the name ‘Mobile Me,’ for devices and services combining features of the iPod with Motorola’s ROKR telephone and the Blackberry portable communications device.

    Appled has asked the patents office to lock in the ‘Mobile Me’ name for handheld devices as well as accompanying mobile telephone service, according to the trademark applications. The telephone service would provide ‘digital music from local or global communications networks’ as well as online databases ‘in the fields of music, concerts, videos, radio, television news’ and more, the applications state.

    maker of the iPod portable music player, could be planning to launch its own mobile-phone venture, recent federal trademark filings indicate.

    The filings reinforce speculation that Apple would wade deeper into mobile phones in the wake of a partnership with Schaumburg, Ill.-based Motorola (NYSE: MOT) , the world’s second-largest mobile phone maker.

    Motorola’s Rokr phone, introduced last year, was the first to feature Apple’s popular iTunes digital music store. The phone has been criticised for inadequacies, but it has still underscored the growing importance of music to mobile phones.

     “I think they will have a device and an MVNO,” said Albert Lin, an analyst with American Technology Research. MVNO is short for “Mobile Virtual Network Operator.” MVNOs don’t own physical networks; instead, they buy wireless time from major networks like Sprint Nextel, and operate under their own brand.

    An MVNO would be a good way for Apple to spur downloads of songs from iTunes through a wireless network, Lin said: “They have to find a way to get over-the-air downloading of iTunes.” The first iTunes phone doesn’t have that capability; users can only download songs directly from their personal computers.

    By operating its own MVNO, Apple would have more control over distributing and pricing its music services than it would in a traditional relationship with a wireless carrier. “They have to take a more proactive role in developing the [over-the-air] market,” Lin said. Apple’s iPod, known for its style and functionality, has become a consumer electronics icon. The Rokr phone was criticized for lacking the iPod’s style and for holding only 100 songs, about 900 less than the new iPod Nano.

    If Apple chooses to make its own phone, it will likely contract out much of the engineering to a firm that specialises in mobile phones, said Ed Snyder, a stock analyst with Charter Equity Research. One Company tipped to pick up the contract are BenQ an OEM manufacturer that recently aquired the Siemens brand. during the recent CES event in Las Vegas a senior BenQ executive did admit that they were talking to Apple about OEM manufacturing. Snyder doesn’t see an Apple phone as a threat to Motorola or other established mobile phone makers. “The mobile phone business is enormously complicated,” he said.

    Phones are primarily sold through carriers. Carrier/manufacturer relationships can be tricky. Lin also said he didn’t see an Apple phone venture as a threat to Motorola: “I think Motorola would view it as positive.” Apple might market an MVNO in tandem with a Motorola phone. And Motorola is likely looking for a better outcome from its Apple relationship than it has gotten from the first Rokr phone, Lin said

    EXCLUSIVE: Harvey Norman Tipped To Be Going After After Dick Smith Move Stores

    A three-way fight is believed to have broken out between major consumer electronics stores for the Dick Smith Move stores, which are seen as one of the few assets in the failed retailer’s portfolio that was actually making money.

    The favourite to snare the stores is believed to be Harvey Norman, who is keen to expand the stores that are generating excellent revenue at Australian airports into overseas locations.

    The man who started the Move Stores is Michael Dykes.

    Convinced that there was a market for premium consumer electronics products Dykes rolled out the new store concept at Westfield Bondi.

    He then migrated the Move the stores into airport where they started to flourish.

    According to sources Apple was at one stage delivering hundreds of products to Move stores prior to the Dick Smith stores collapsing. 

    Speculation is mounting that Harvey Norman is in the process of taking over the Monster power brand products via a Company that Gerry Harvey owns 60% of the shares.

    The Company has not commented on the Move Stores or Monster.

    Currently Monster products are distributed by Sydney based Convoy.

    CE Advertisers Move Online

    Vendors, retailers and distributors of consumer technology are turning to online marketing due to the sheer volume of consumers who search for goods and services online say Price Waterhouse Coopers.

    Vendors, retailers and distributors of consumer technology are turning to online marketing due to the sheer volume of consumers who search for goods and services online say Price Waterhouse Coopers.


    They also report that Internet ad revenues rose 11% in the third quarter from the same period a year ago, and 2% from the second quarter. The research was conducted in partnership with the Internet Advertising Bureau (IAB).
    In both Australia and the USA online ad revenue have risen significantly as advertisers search out niche markets and easy measurement of their campaigns.


    During the past 12 months web sites like SmartHouse and ChannelNews which are independently measured by both Nielsen Digital and Google Analytics have witnessed significant growth. SmartHouse has grown by 18% to over 3,500,000 unique visitors this year to date. ChannelNews which is a trade only web site has risen by 14%.


    “The growth of interactive advertising that we’ve been experiencing over the past few years has stabilized due in large part to the difficult current economic climate,” said Randall Rothenberg, president and CEO of the IAB.


    David Silverman, a partner at PricewaterhouseCoopers.” The Internet should be better poised to withstand the current economic storm given its ability to combine performance-based advertising along with broad-based branding.”

    Fallout From Dick Smith Hurts CE Industry, For Sale Sign Out At One Distributor

    The fallout from the collapse of the Dick Smith retail chain is being felt at CES as several big brands question the exposure of their distributors in Australia.

    ChannelNews has been told that one major distributor of consumer electronic products is already up for sale with several parties having already met with management to discuss either an investment or acquisition. The distributor is exposed due to the collapse of Dick Smith by over $800,00.

    Among the Companies who have expressed an interest in the distributor is Harvey Norman.

    One distributor who is believed to be exposed for over $7M is Yale Prima who was also involved in a messy recall of cables involving Bunnings. It’s believed that Yale Prima does not have insurance cover on the exposed stock.

    Another distributor now under pressure is NSW Based organisation Roadhound who insiders claim is exposed for almost $15M dollars.

    Also exposed is Philips and Gibson brands, ChannelNews understands that their product was supplied direct and not via Powermove who has the rights to sell Philips audio products in Australia.

    Another brand exposed is Toshiba with one insider tipping that the collapse of Dick Smith who were a major buyer of Toshiba consumer PC’s could accelerate the exit of Toshiba from the Australian market. 

    Analysts at CES estimate that both JB Hi Fi and Harvey Norman are set to benefit from the fallout with both retailers set to benefit from a surge in first and second quarter sales in excess of $200 Million dollars.

    According to sources Dick Smith receivers have already had an offer for the Move Stores and the Dick Smith online operation however there appears to be a problem over the rights to the Dick Smith name being used in a separate online store.

    According to The Australian, analysts said the likelihood of a buyer emerging for the business – with 393 stores across Australia and New Zealand – was low, with store closures and industry consolidation a more likely scenario. Morgan Stanley analysts forecast a 9 per cent increase, to $241.2m, in earnings for Dick Smith rival JB Hi-Fi in 2017, while Harvey ?Norman could benefit with a 4 per cent rise to $480m.

    Those retailers could suffer in the short term if liquidators move to clear Dick Smith stock, although their bargaining position with suppliers could improve, delivering profitability increases of as much as 20 per cent, they said.

    However, several product lines were already unavailable for ?purchase at several Dick Smith stores, with one company insider suggesting a failure to secure bridging ?finance would scupper any chance of trade continuing as normal.

    Analysts at Citi suggests the closure of about 100 Dick Smith stores could deliver JB Hi-Fi an earnings uplift of about $19 million in 2015-16.

    Analyst Craig Woolford said the closure of up to half the Dick Smith stores was the most likely outcome of the administration “with a new owner taking a business with 25 per cent to 50 per cent fewer stores.”


    Mr Woolford said if Dick Smith shut down 100 outlets, which is equivalent to about 25 per cent of the retailer’s 393 store network, its sales base would fall by about 20 per cent.

    “We think JB Hi-Fi could capture 50 per cent of sales lost through Dick Smith store closures, this would be $106 million or a 2.8 per cent boost to like for like sales growth,” Mr Woolford told the Sydney Morning Herald. 

    “We estimate a total EBIT gain of $19 million in fiscal 2016, comprising $11 million EBIT from additional sales captured and $8 million because discounting in the industry reduces by 20 basis points.”

    Mr Woolford said JB Hi-Fi would be the biggest winner from Dick Smith store closures because of the geographical proximity of the competing store networks and the similarity of their product lines.

    J P Morgan said Dick Smith was likely to consider the closure of unprofitable stores as part of the voluntary administration process as was an exit of its alliance with department store David Jones.


    One high profile retailer, who would only talk anonymously blamed Dick Smith’s demise on clever accounting according to Fairfax Media. 

    “They used every accounting trick in the book, whether you pay for it this year or next year you always pay for it eventually,” he said.

    “$1.5 billion worth of value has been destroyed if you add up what Woolworths wrote off in the sale, what the banks are now owed, the debt to unsecured creditors and the float, it’s a $1.5 billion hole.

    “And they haven’t built any market share or customer equity.”

    Samsung D600 Given Makeover

    UPDATED: Samsung has given their D600 a new paint job by chrome-ing its curves. But it wont fix the problems on the inside.

    The Samsung D600 is one of those phones that reminds of that great piece of fruit that looks sensational on the outside but when one sinks your teeth into it you find out that it’s rotten on the inside. Now in Chrome the Samsung D600 is a 2 megapixel sliding cameraphone with Bluetooth. It has a large 88 MB memory capacity, which Samsung claims is enough to store 1000 megapixel photos or 20 of your favourite songs in MP3 format.This phone does look great with its easy slider big keys and a bright screen, but it is let down by lousy Samsung PC Studio software that makes connecting to Outlook difficult which ever way one tries. Then there is the issue of entering data into a contacts database.

    There is no space for an address and no space for a business title. Maybe the Korean designers have never been on the road doing sales calls and have to access a phone to check an address. Key today in any phone purchased by a business executive is the ability to easily access Outlook and download contacts. With the D600 we constantly got an error message that said “The connection to the Microsoft Exchange server is unavailable. Outlook must be online or connected to complete this action”. In the testing that we did, Outlook was  connected, yet despite this it was impossible to easily connect to the Microsoft Exchange Server both live in a corporate network where Outlook is always connected or in a stand alone setting from my home PC. 


    Click to enlarge

    Then there is the issue of downloading music. This phone is slow, real slow. To download 10 songs that were on average three minutes in length took more than 45 minutes.

    Another problem I found was that a user cannot listen to music and write notes at the same time. A visit to several comment Websites revealed the following complaints about the Samsung D600. All the comments came from consumers who have purchased the phone. They said:

    No airplane mode “I’ve tried everything to try and turn the cellular functions off so I can just use the mp3 player but to no avail”. Delayed ring tone “The mp3 ring tones have a slight delay before they are audible. I don’t know if the secret menu can be tweaked to fix this problem”.

    No vibrate & ring at the same time – “Sometimes I have it in my pocket in a busy place and I don’t hear it ring. It would be great if it could vibrate at the same time.

    No silence button – It says that if I press a certain button it mutes the call, but it only rejects the call even when I change the option to mute the call”. No automatic save and send SMS. Cannot assign mp3 ring tones to SMS tones and can’t change volume of the message tone (unless you use the secret menu option). No multi tasking – “you can’t listen to the mp3 player while you are text messaging someone or receiving a text message”.

    The feature set for this phone is very impressive. There’s a Trans-Flash memory slot for upgrading the memory to 512 MB. It has stereo speakers, which compliment it’s ability to playback MP3 music files and MP3 ringtones. It’s a tri-band phone with Java support for games and other applications plus PC connectivity and synchronisation by Bluetooth or USB data cable.

    So if you want a mobile phone that looks great and will knock the socks off any other phone in the looks stakes the answer is the D600. But if you are looking for functionality, forget it.


    Click to enlarge

    Samsung D600

     

    Price: Changes with carriers

    Rating: 2/5

    Apple MacBook Retina Display Problems Has Users Up In Arms

    Tens of thousands of Apple MacBook owners are up in arms over what appears to be a fault with the retina display screens in Apple notebooks.

    Apple who loves to brag as to how good their gear is, has been accused of failing to address the issue after uses went online to report “horrific stains” spreading across screens, in the forms of spots and patches.

    Apple who uses Sharp and LG to manufacture their display screens has already replaced several display screens on MacBook’s however thousands are claiming that they still have problems with lawyers now setting up web pages with a view to starting a class action.  

    Phi Chong, a software engineer, told the BBC he has had to replace his screen twice in the last two years. He said he had been told Apple would not carry out further screen repairs.

    Apple claims that Australians facing the problem should contact its Apple support centre.

    Users who have been affected are concerned they will face expensive service fees once their warranties and/or extended AppleCare protection plans expire.

    “My last screen replacement had its anti-reflective coating start peeling off within a month,” said Phi Chong.

    “I’m worried it will start peeling again after my AppleCare has expired.”

    A website called “Staingate” has been set up by a group unhappy with Apple’s response.

    Some Apple MacBook owners have been told they will have to pay $800 for repair work, the Staingate website states.

    A Facebook group formed by people experiencing problems with their MacBook screens has 1,752 members, and Staingate claims to have been contacted by more than 2,500 people so far.
    US legal firm Whitfield Bryson & Mason has contacted the Facebook group offering to investigate and potentially set up a class action legal case to take on Apple. 

    The group has also set up a petition on the Change.org website which asks Apple chief executive Tim Cook to “take immediate action” to address the issue.

    Some people say problems with the screen can start appearing within a few months of purchasing the laptops, with the 13in (33cm) display screen.

    While many people on the Facebook page are reporting that Apple stores around the world – including in Australia and New Zealand – are agreeing to carry out free screen repairs outside the warranty period, others said they had been told it was “cosmetic damage”, which is not usually covered.

    Apple has not confirmed whether there is an issue with the screens, or what might be causing the damage.

    Its 2013 models seem to be worst affected, but there are online forums discussing the problem dating back to 2009.

    “Customers who experience problems with their Apple products should contact AppleCare,” a spokesperson for Apple said.

    EXCLUSIVE: Two More Senior Execs Quit Dick Smith Along With Institutional Investors

    EXCLUSIVE: Two more senior executives have quit Dick Smith with financial institutions now calling for management changes at the top of the struggling retailer, whose shares were still being punished for downgrading its profit guidance.

     Its shares fell more than 10% to $0.695, on top of yesterday’s pummeling and a 30% fall earlier in the week.

    The latest executives to quit are Greg Hirsh merchandise manager who oversaw 2 buyers, also quitting the embattled retailer is Chris Borg, General manager of merchandise planning.

    Earlier in the month 3 buyers along with Carl Bonham the former merchandising director quit Dick Smith. Before that Rod Orrock the former General Manager quit for health reasons.

    ChannelNews understands that Greg Hirsh is heading to Masters and Borg to the Wesfarmers Group where he will work at Target in a senior role. 

    Last week Dick Smith management circulated an email to staff warning them about talking to the media about conditions at the Company.
     
    We have also been told that at least one other senior executive is talking to a retailer in the consumer electronics market.

    On Friday several institutions moved to dump their shareholding in Dick Smith among them AMP and Deutsche Bank.

    One Institutional shareholder told ChannelNews that the board was being pressured to dump current management who have been responsible for a significant slump in profits.


    Click to enlarge
    Nick Aboud CEO of Dick Smith is under pressure from Institutional Investors


    Recent announcements by the Company show that like-for-like sales in the month were down 5.0%, with poor marketing decisions seen as producing a large reduction in traffic through Dick Smith stores who are now stocking a large volume of house brand products that the Company is struggling to clear. 

    These private label products while having margins of more than 80% fail to deliver sales growth and are often an after though purchase.
     
    Executives claim that promotional activity will now be ramped up in an effort to shift stock a move that some observers claim will lead to a discount ting war weeks out from Xmas.
     
    Senior staff have told me that buyers who have been stopped from buying branded products because of cash flow problems are concerned that the business is “top heavy” with house brand products Vs in demand branded products.
      
    Several Institutional investors believe that the current Dick Smith strategy raises doubts about the underlying capacity of the business
    Many people believe that Dick Smith is struggling to differentiate itself from competitors and improve returns on invested capital to sustain medium term growth.

    FNArena said recently that margin headwinds are being driven by higher promotional intensity and the cost of obtaining growth is rising.
     
    There is also a bigger structural issues for the Company Claim’s Deutsche Bank who earlier today dumped shareholdings in the Company. 

    They claim that omogenous products such as phones and tablets, and some TVs, are being sold widely by manufacturers as well, and price competition is fierce. Discounting is often led by one business and routinely followed by others to protect their market share, which means improving foot traffic may not be easy.

    Deutsche Bank notes the strong phone sales in September following the launch of the new iPhone, although these are low-margin sales that are not contributing to profit growth. 

    Short Throw Projectors Taking The Place Of The Flip Chart

    Projector sales are starting to take off again according to GFK with a core market being the office and the purchase of short throw projectors. Sean Fellows, Account Manager at GfK Retail and Technology said “Projectors are now likely to become the flipchart of the digital age consequently demand is likely to continue to rise in the future.”

    Projector sales are starting to take off again according to GFK with a core market being the office and the purchase of short throw projectors. Sean Fellows, Account Manager at GfK Retail and Technology said “Projectors are now likely to become the flipchart of the digital age consequently demand is likely to continue to rise in the future.”

    He also said that changing working practices and budgets are resulting in a need for greater flexibility within the office. As a lot of work is done digitally, when coming to share this information the need for Projectors and other Audio Visual Equipment has increased.

    While GFK Australia does not track office or SMB sales other than via mass market retailers research from IDC and Gartner does show that the SMB and large enterprise market are buying a new generation of projectors.

    In the UK projectors have witnessed growth of 38% against the same period last year.Despite the impressive sales of Projectors in 2008, there has however been a noticeable slowdown in unit sales over the past couple month as business cut capital budgets.

    Whilst businesses may aspire to equip all their meeting rooms with fully fledged AV suites, within the current trying economic climate this may be no more than a pipe dream. Many meeting rooms are simply too small to deploy a traditional projector with any real success and with a workforce becoming more mobile with the adoption of increasing numbers of Laptops, Netbooks and Smartphones, business technology is becoming more flexible and data projectors are certainly not slacking.

    The value of a Portable Data Projector in the mobile workplace is incredibly advantageous, even more so for the new generation of Short Throw Projectors coming onto the market. A Short Throw Projector is defined by GfK as being able to produce an image larger than 1.5M from a distance of less than 100cm. With the benefit of portability and the added bonus of being less reliant on having boardroom sized meeting rooms in which to present, the flexibility of Short Throw Projectors makes them desirable to 21st Century business. This also has benefits within the home as living rooms are getting more compact and space is limited for large screen Televisions.

    The latest GfK figures for the UK show that the volumes sold of Short Throw projectors are indeed on the rise, growth increasing on a month by month basis. In September 2008, 2.3% of all projectors sold within the UK were Short Throw models. Interestingly this growth is fuelled by new LCD Projectors, with 5.6% of all LCD Projectors sold in September being Short Throw models, compared to DLP models which contributed to only 0.2% share in the same period. This goes completely against the trend when one looks at the prevalent technologies in the total Projector market. DLP Projectors, which have the greatest volume share standing at 61% in September 2008, have grown massively from the position 12 months ago where they accounted for only 43% of volume sales and LCD Projectors dominated the market.

    Sean Fellows said  “The adoption of Short Throw Projectors within the consumer channels are yet to be felt – probably due to the high average selling price. The price elasticity of demand is less elastic within a business than in the consumer market. Consequently the average consumer is unlikely to see the intrinsic benefits when they are able to purchase a large screen Television at a lower price. Nevertheless as the technology becomes increasingly price competitive, a fall in price by a couple of hundred pounds could see the adoption rate rise within the consumer channels”.

    CE Retailers To Benefit Most From Rate Cut

    Consumer electronics retail got a double stimulus today with a 1% interest cut and an announcement by the Federal Government that they are set to pour more money into the economy in an effort to prevent Australia’s economy from sliding into a recession.

    Early this afternoon the Reserve Bank of Australia slashed interest rates to 3.25% a move that executives at JB Hi Fi and Harvey Norman welcomed.

    “We anticipated 1% and we believe it will help retailers,” said David Ackery the General Manager of Electrical at Harvey Norman.

    Earlier in the day the Federal Government announced that they are set to tip more than $40 billion into infrastructure, public housing, business tax breaks and one-off cash payments in an effort to prevent the Australian market going into recession.

    Kay Spencer the CEO of NARTA, one of the largest buying groups in Australia said, “We saw what the boost to the economy did for the consumer electronics industry over the Xmas New Year period and I believe that the latest cuts to interest rates along with further Government stimulus will help the industry.”

    On the issue of discounting and stock levels she said, “Several retailers are suffering from a lack of stock. There are a lot of back orders in the channel however we do need further price increases and we need margin kept in the channel going forward”.
    She added “We also need new models and new products to motivate consumers to spend” she said”.

    Currently the Federal Government is forecasting that unemployment will rise to 7 per cent next year a move which Gerry Harvey the Chairman of Harvey Norman said recently would lead to a recession.

    Prime Minister Kevin Rudd says the plan provides a basis to see Australia through the crisis, but he has conceded it will not eliminate the country’s economic woes.