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

    Smart Office

    Commission Rejects Need For Inquiry Into Samsung Washing Machine Recall

    Samsung has had a major victory, over consumer group Choice and a select group of former customers, who were calling for a State inquiry into a Samsung washing machine recall.

    Assistant Commissioner Mark Whybro decided against the move, saying the number of incidents was declining and the recall response rate was “greater than usually expected”.

    “A significant manufacturer recall program is in place … and the small number of fires in repaired machines – [we’re] aware of three [in NSW] – may have been caused by incorrect repair procedures by contractors,” he said.

    Samsung who have been working directly with the Australian Competition and Consumer Commission to track down owners of the faulty Samsung washing machines have had to put up with a barrage of complaints and stunts by organisations such as Choice, despite both the ACCC and Samsung mounting a major national campaign to alert customers to the problem machines. 

    Despite massive exposure across national media 40,000 owners of the potentially faulty Samsung washing machines out of 144,451, have so far failed to inform Samsung or contact the ACCC.


    More than 80% of the consumers who purchased an affected Samsung washing machine failed to fill in a warranty card or go online to register their purchase which is a major problem for not only Samsung but the entire appliance industry. 

    74 per cent of the 144,451 machines have already been remedied by Samsung. 

    This was not good enough for Choice, who chose to crush a Samsung washing machine in a cheap publicity stunt, over lobbying the Federal Government to fund the setting up of a mandatory national database system, that puts the onus on resellers to register the sale of all electrical goods that are connected to a live power source or charger.


    Tarnya Allen and Di Fisher – founders of a 2700-member strong Facebook group of affected machine owners – said the rejection of their inquiry request was a “slap in the face”.

    Fairfax Media claimed recently that under NSW laws, the Fire Commissioner can request the State Coroner to hold a mandatory public inquiry into specific fires and explosions, even if there are no deaths.

    A spokeswoman for the State Coroner said there had been no mandatory public fire inquiries since Michael Barnes took the top job two years ago.

    “Product recalls such as the Samsung one are normally investigated by Fair Trading. 

    There have been no product recalls referred to the Coroner’s Court over the past two years,” she said.

    Fair Trading, the authority overseeing the national recall, approved the plastic-bag-and-tape rework which appears to be working. 

    In March, Samsung announced it would implement a new, multi-pronged strategy to prevent further fires and other incidents.

    Samsung urges consumers who may be affected by the recall to visit samsung.com/au/washingmachinerecall or call 1800 239 655.
    The following models are impacted:
    SW75V9WIP/XSA
    SW65V9WIP/XSA
    SW70SPWIP/XSA
    SW80SPWIP/XSA
    WA85GWGIP/XSA
    WA85GWWIP/XSA

    Why I Would Think Twice About Signing Up For Telstra’s Cloud Based Office 365 and Microsoft Dynamics Service

    If you are a small medium business and you are thinking of moving to Telstra’s offering of cloud based CRM Dynamics and their Microsoft Office 365 suite of applications, I would seriously think twice about their offering if our experience is anything to go by.

    The first thing Telstra executives wanted to do when we signed up for a five licence package was charge a credit card with the cost of getting the service.

    Now three weeks later and after more than 40 emails and numerous calls to Telstra we still do not have our five CRM licenses up and running, in fact all we have is a cost to a credit card.

    The first problem we encountered was Telstra’s inability to deliver an Outlook email service via their Office 365 service which is significantly more expensive than what US users are charged, in fact it is more expensive than what Microsoft Australia is now selling their Office 365 licences for via JB Hi Fi.

    Telstra have come up with excuse after excuse, they blamed our firewalls and DNS settings despite the fact that we already had an Office 365 licence via Microsoft and not Telstra and this working perfectly. 

    We also have several licenses of Adobe’s cloud Creative Suite service and that too was working perfectly via our existing server infrastructure. 

    We also deliver content direct into retailers web sites via cloud based services. 

    The reason that we had to go to Telstra to get the services we required was because we had chosen to drop Salesforce for the simple reason it is expensive and lacks a lot of the features that Microsoft’s Dynamics CRM delivers.
     
    Unfortunately Microsoft Australia have a sweet heart deal with Telstra that allows the carrier to exclusively sell the Microsoft Dynamics CRM service to small medium business in Australia and the minimum licence is five. This strategy eliminates over one million Australian businesses such as small retailers, real estate agents and small service businesses who do not want the cost of five licences a month.

    This is despite the fact that it is these type of businesses that need mature CRM systems that easily interface with accounting packages and Microsoft’s Office 365.

    Six days ago we were told that it would only be a matter of hours “defiantly by the end of the day” that our service would be up and running, that was Thursday of last week.

    What Telstra claim in their marketing is that “Big companies have been able to afford the sophisticated CRM of Microsoft Dynamics for years. For many, it’s the secret of their CRM success”.

    “Now, any sized business can afford to use this powerful platform. You’ll get sales, service and marketing all in one neat package, with options of 5GB or 30GB storage. For smaller businesses, it’s a golden chance to combine agility with ability and deliver outstanding, responsive customer service to the highest standards”

    Really, maybe Telstra needs less marketing spin and more service if they are to be believed. 

    Consumer Technology A Big Winner For Woolworths

    Woolworths has cranked up their consumer electronic operations in an effort to capitalise on the growth being recorded by most CE retailers. In their latest report to the ASX Woolworths has recorded consumer electronic sales of over $400 million for the first quarter of FY09

     

    Their success follows a major restructuring of their Dick Smith operations. However Woolworths have warned that the Australian market is slowing.

    In the 14 week period ending 5 October 2008, Woolworths CEO Michael Luscombe said that CE sales had increased when compared with the same period last year.

    “This is a good start to the financial year and particularly pleasing is the continued momentum in our Australian operations, with an overall improvement in comparable sales growth.” Luscombe wrote.

    Consumer electronic sales increased from $365 million for Q1 FY08 to $408 million for the first quarter of this current financial year a growth of 11.8 per cent year on year.  Last year sales grew from $347 million to $368 million, a $21 million or 6.1%.

    “Consumer electronics has continued to enjoy solid growth with sales for the quarter increasing 6.1%. Comparable store sales increased by 4.9%…during the quarter, an improvement on the 3.8% recorded in the fourth quarter of 2008, reflecting an improved result from our Australian operations,” wrote Luscombe.

     

    Overall Woolworths saw sales growth accelerate to 9.6 percent in the first quarter as food and liquor sales withstood slower consumer demand, and it maintained its full-year sales outlook.

    Sales growth rose from the 7.5 percent reported in the fourth quarter, boosted by store refurbishments, the company said.

    Woolworths Chief Executive Michael Luscombe said that subject to the uncertainty about discretionary spending, full-year sales growth would be in the upper single digits, as previously forecast.

    Consumer spending in Australia has slowed sharply this year, but economists say there are signs confidence improved after the central bank unexpectedly slashed official interest rates by 1 percentage point this month in response to the global financial crisis.

    Woolworths’ same-store sales in Australian food and liquor rose 6.0 percent, up from 4.9 percent in the fourth quarter.

    Myths Of Mobility

    Nokia believes true mobile working should be extended down the organisational chart. A new study, Myths of Mobility, says employees in the office may spend more than a third of their time away from their desks.

    “While corner office executives may have access to mobile e-mail, more than half your workforce uses mobile technology to some extent or other for work,” the report says. “There is a need for mobile technology in business.

    Organisations can support this level of mobility in a cost-effective, secure and easily deployed manner.

    “Misconceptions abound about what can and can’t be achieved. The report aims to educate businesses about these misperceptions and inspire them to investigate the issues around enabling workforce mobility.”

    “Many decision makers believe that mobility is not cost-effective, not secure, and is a nightmare to manage. These notions are based on assumptions from fragmented sources and second-hand comments, and we hope to help clarify today’s mobile landscape with this report,” says Mathia Nalappan, Nokia’s VP, Asia Pacific.

    “Customers are reaping the benefits of mobility, seeing return on their investments, an increase in productivity, and improved employee morale.”

    Myths of Mobility is available on www.nokia.com/nokia/0,8764,330,00.html

    Clive Peeters Share Rocket 11% As Market Slides 4.7%

    Shares in Clive Peeters have bucked a 4.7% downturn in the ASX by rising 11.1% in late afternoon trading. However by the close they had lost all their gains to close at $0.90.

    Late yesterday the Company finally admitted that they were calling in KPMG Corporate Finance in an effort to solve their problems which has resulted in their shares nose diving 94% this year.

    Shares in Clive Peeters have bucked a 4.7% downturn in the ASX by rising 11.1% in late afternoon trading. Late yesterday the Company finally admitted that they were calling in KPMG Corporate Finance in an effort to solve their problems which has resulted in their shares nose diving 94% this year. 
    First tipped by the Financial Review last Tuesday the Company has finally confirmed that they have called in KPMG to conduct a “strategic review” of their operation. Insiders have also said that the Company is cutting stock levels in an effort to preserve cashflow. 
    In recent weeks GIO who have the lion’s share of the consumer technology credit risk market have admitted that they are keeping a close watch on the Company’s performance.
    In a statement to the ASX the Company said that they are set to consider both corporate and operational strategies with a view to maximising value for all shareholders”.
    Clive Peeters said it has retained KPMG Corporate Finance to advise the process and it does not intend to disclose any details of the review until it’s completed.

    Australians Are Better Off And Will Spend Say Citigroup

    Australians in particular those with families are better off now than they were before the economic downturn claims Citigroup one of the world’s leading investment banks. Mortgages have fallen by 2% Petrol prices are at an average of $1.04 Vs $1.41 twelve months ago and share market dividend yields have risen substantially. This they say has added $75 to the average weekly income.

    They also claim that drawing conclusions about the “average” Australian can be dangerous. Only 35% of Australian households have a mortgage. 29% are renting and 45% of Australian households are families with children, while 20% are retirees.


    They say that families with children are better off by $119 a week because they have benefitted from Government handouts. Retirees and renters have a substantially smaller benefit.


    Citigroup’s recent report claims that retail sales growth was solid in January with the strongest growth recorded in food retail. Supermarkets, takeaway food outlets plus restaurants and cafes were all stronger. The sharpest slowdown was in appliance retailing and footwear sales.


    They say that mortgage debt is concentrated within 35% of Australia’s population however they do predict that Australian households are facing a number of challenges with rising unemployment and falling asset prices.


    However, both the RBA and Federal Government are cushioning the average household through interest rate cuts and tax cuts, especially for families.


    The say that the recent Federal Government fiscal stimulus for households will boost household income by an average of $2,000 per household because of income tax cuts, tax bonuses and payments for seniors.

     

     

    Citigroup say “We estimate the average Australian household’s income will be $75 per week higher in 2009 compared with 2008 (+$3,881 per year). Families with children are even better off, with an increase in income of $119 each week. We arrive at these estimates by rolling forward the expenditure that each demographic group had in FY04, as reported in the ABS Household Expenditure Survey.”.

    The net increase in income is highest for families with children (45% of all households) and smallest for those retirees reliant on investment income with fewer benefits from mortgage rate reductions and tax cuts.


    Currently unemployment is at 4.8% and is likely to rise to at least 5.4% by the end of 2009. Each 1% rise in the unemployment rate reduces income dramatically for a family, but the overall impact on household income is 1%. The bigger concern with unemployment is weaker consumer sentiment and a fall in the willingness to spend.
    While disposable income is rising for households, the high level of debt and uncertainty has resulted in an increase in savings, detracting from retail spending. The household savings rate jumped to 8.5% in the December 2008 quarter compared with virtually no savings in 2007.

    They conclude by saying “Consumers have never had it so good. For the past fifteen years, retail prices have not kept pace with overall inflation.” They also conclude that Companies like Harvey Norman and consumer electronics retailers will benefit from Government stimulus programs however appliance sales are expected to remain flat.

    Dowload the full report here.

    Dodgy Accounts, Three Years Supply Of Batteries + Overseas Dick Smith Companies Probed By Investigators

    Investigators probing the accounts of Dick Smith are believed to be questioning the cost that Dick Smith was actually paying for house brand goods from Companies located in Asia.

    They are also probing as to how Dick Smith came to be stocking, three years supply of Dick Smith branded batteries and other house brand products supplied by related Companies located in Hong Kong when they were placed into receivership.

    Anchorage Capital Partners – the private equity firm that transformed Dick Smith from a $94 million, middle-market electronics chain into a $520 million stockmarket star – will have to front a Senate inquiry to answer questions over its role in the chain’s subsequent demise.

    SA Senator Nick Xenophon claims it would be up to the Senate committee to decide who will appear before its inquiry into the collapse of listed retailers in Australia, but he expected Anchorage would be questioned.

    “The collapse of Dick Smith has raised some fundamental questions about private equity and I’m sure private equity will defend its position vigorously,” Xenophon said.

    Questions are also being raised about the actions of Dick Smith Chairman Rob Murray who in a filing with the Federal Court has failed to supply a detailed report relating to the final days at Dick Smith or how Dick Smith auditors Deloitte who were also the accountants for Woolworths the prior owners of Dick Smith failed to reveal shortfalls in the Companies accounts. 


    Click to enlarge
    Dick Smith Chairman Rob Murray


    According to the last lot of known financials for the Company, operating cash flow for the last financial year ending June 28 was negative.

    Records show that Dick Smith had $29.5 million cash, $53 million of receivables due, and payables totalling $228 million.

    To date most of those suppliers owed money have not been paid.

    Recently Ferrier Hodgson approached suppliers offering a bank guarantee to resupply Dick Smith, some vendors have taken up the offer however several brands including the likes of Samsung are refusing to give rebates for goods sold. 

    Recently The Federal Court judgment, granting Dick Smith’s administrators a six-month extension on holding its second creditors’ meeting.

    According to court documents, the Dick Smith directors, including chairman Rob Murray and former chief executive Nick Abboud have failed to report shortcomings in the accounts, as a result of requests by the directors, the administrators have granted an extension to the directors until 29 January, 2016, however the directors have come back requesting a further extension until 19 February, 2016. The request is currently under consideration,” said the court document.

    Currently various organisations including Ferrier Hodgson are feasting off the Dick Smith carcass.

    As the company crashed, Dick Smith’s directors assigned no less than four partners of McGrathNichol as administrators. Its banks appointed three partners of Ferrier Hodgson as receivers, they charge out at a minimum of $600 an hour. 

    While they will get their fees suppliers have little chance of being paid. 


    Last week a smoking gun email found on the Computer of former Dick Smith Finance Director Michael Potts and addressed to former CEO Nick Aboud revealed a $2 million shortfall in annual leave entitlements.

    McGrathNichol have already spent Dick Smith’s money to obtain a six-month extension from the Federal Court.

    According to Jeff Knapp, accounting academic at UNSW who was called in by Fairfax Media to analyse the Dick Smith accounts. 

    Rather than charging $600 an hour per partner and taking six months, Knapp took two hours and 14 minutes.

    Based on the fact that on November 30, 2015, Dick Smith Holdings announced a $60 million write-down of inventory which had become necessary. The share price tanked and the banks soon pulled the pin.

    Dick Smith Holdings has failed because of an inventory problem; and this inventory problem can be traced to a time before the company was floated on the ASX Knapp concluded. 

    On November 26, 2012, Anchorage Capital, acquired the business from Woolworths using the entity Dick Smith Sub-Holdings Pty Ltd – but the financial reports of Woolworths and Sub-Holdings for June 2013 paint a very different picture about the acquisition.

    The fine print in the 2013 report of Dick Smith Sub-Holdings show the inventories as having a book value of $371 million to Woolworths at the date of sale.

    In contrast, the Woollies’ annual report records the inventories of subsidiaries as having a book value of $246 million, and these inventories include more than just the Dick Smith business.

    According to Anchorage, it valued the inventories down by $58 million to record a cost of acquisition of $312 million. If the Woolworths number is correct, however, then Anchorage actually valued inventories upwards by at least $66 million.

    It is a similar tale for plant equipment. According to Anchorage, it valued plant and equipment down by $55 million to record a cost of acquisition of $65 million.

    Based on the Woolworths number though, there was actually a revaluation upwards of $14 million.


    Fairfax Media point out that Deloitte Sydney acted as auditor for Woolworths and Anchorage for 2013 – not to mention, ahem, “Investigating Accountant” for the public float. If this Big Four audit firm, like its peers, was not beyond the law things would get very messy.

    The Woolworths financial report also shows Dick Smith’s asset write-downs and restructuring costs of $420 million were booked for June 30, 2012.

    It appears that when Anchorage came along it bumped the inventory and plant values back up and misleadingly disclosed it had done the opposite.

    The most obvious thesis is that Anchorage “window-dressed” the inventory balance of Dick Smith when it acquired the business and the inventory remained window-dressed until the company failed.

    This suggests the inventory balance shown in the prospectus for the float is false. If this is the case, those who bought shares in the float, mostly super funds, have been played for mugs Fairfax claimed. 

    The inventory saga came unstuck in 2015, long after the crew from Anchorage had pocketed hundreds of millions of dollars by tipping their shares into the superannuation system.

    Nick Aboud the former CEO of Dick Smith is back working with the guys from Anchorage Capital however insiders in Mosman where he lives in a palatial waterside property, say that he has become deeply concerned with what ASIC and various investigators are discovering. 

    Several people have concluded that the statutory disclosures at the time of the acquisition simply do not reconcile – and there is therefore scope for regulators to act without waiting for six months.

    Michael West writing for Fairfax Media claims that questions need to be answered about the actions of Dick Smith and Anchorage management.

    Why does Anchorage extol the value of the Dick Smith brand in the prospectus when it has not recognised an asset for that brand at acquisition date?
     
    Why would Woolworths sell the Dick Smith business for $115 million if Anchorage and its auditors, Deloitte, reckoned the fair value was $261 million? 

    Why did Woolworths earn another $118 million for “administration” costs in the Dick Smith changeover?  Is there a conflict of interest when Deloitte, the auditor of Woolworths, is also appointed as Anchorage’s auditor and investigating accountant for the prospectus?

    New Toshiba Notebooks

    Toshiba ISD has introduced three new models to its Portege, Libretto and Tecra ranges. All include Toshiba EasyGuard, a new suite of hardware and software tools that provide data security, system protection and connectivity to notebook PCs.

    Toshiba’s new notebook
    Port_g_ R200

    The Port_g_ R200 measures 9.9 mm in height, weighs 1.29 kilograms and has a battery life of four hours and 40 minutes, made possible through the inclusion of an Intel Pentium M Ultra Low Voltage processor and specially-developed low-power enhancements. It also features a ruggedised magnesium alloy casing for style and protection of the notebook, and a full range of expansion capabilities and connectivity options making it one of the most portable productivity devices available.

     Libretto U10

    The Libretto U10 marks the reintroduction of Toshiba’s ‘miniaturised’ fully-functional notebook PC range, the Libretto.  At 210×165 mm (width x length) and less than a kilogram in weight (980 grams), the Libretto is roughly the size of an A5 notepad.  Despite its size, Toshiba has included some of the Qosmio AV-note multimedia functions into the Libretto U10. These include:  

    A TruBrite widescreen display with LED backlighting;
    A quick boot function to enable people to immediately watch DVDs or play CDs without having to boot the computer via its operating system;
    A specially-designed DVD dock that features a DVD Super Multi drive (bundled as standard).

    Tecra M4

    The Tecra M4 tablet PC includes Intel’s newest mobile technology (a 2.13GHz Pentium M and Mobile Intel 915PM Express chipset), healthy 80GB HDD, and a nVidia 128 Mb graphics card. The 14.1″ screen offers 1400 x 1050 resolution, with a 145 dpi ratio to allow more light to be transmitted for a clearer viewing experience. Toshiba has also included a thin protective polycarbonate panel to protect the screen whilst it is in use and provide users with a ‘natural’ writing experience.

    The Tecra M4 uses a DVD SuperMulti double layer read/write drive in a Slim SelectBay which supports a number of different devices including a second battery, second HDD or weight-saver. It also includes a full range of connectivity options including wireless 801.11 a/b/g, two USB2 ports, to make it easy to share and save information between devices.

     Toshiba ‘EasyGuard’

    EasyGuard enhances data security with feature such as a biometric fingerprint reader, HDD shock and vibration protection, encryption and digital signature implementation through the inclusion of both hardware and software and a software device lock that enables locking of devices to prevent unauthorised access and theft of data, as well as an external Kensington lock for physically protecting the notebook from attempted theft.

     RRP Pricing

    Libretto U10 with a bundled DVD SuperMulti dock: $3,450. (this notebook is available exclusively to Harvey Norman until late July)
    Port_g_ R200: $3,520
    Tecra M4: $4,950.
     

     

    Vodafone + Optus Cop Most Complaints About Their Network

    Vodafone and Optus are still top of the pops when it comes to complaints about their network and service operation.

    A new survey reveals that Optus scored the highest level of complaints

    between April and June 2015 with 8.5 while Vodafone scored 6.3. In comparison

    Telstra scored 6.0 while Pivotel was the lowest with 1.8.

    Vodafone who has often been referred to as the “worst”

    carrier network in Australia with tens of thousands deserting the network for

    Telstra is still struggling to build their base back up with the introduction

    of new networking capability.

    Complaints made to the TIO as a proportion of telcos’ services

    in operation are at their lowest rate in 18 months, according to the

    Telecommunications Complaints in Context report released today.

    The result for all participating providers, 6.5 complaints

    per 10,000 services in Operation (SIO), has decreased 9.7 per cent when

    compared to January-March 2015 (7.2) and 14.5 per cent when compared to April

    to June 2014 (7.6).

    These results reflect the overall decrease in TIO new

    complaints, which reduced by 10.5 per cent during 2014-15. This is the lowest

    level of new complaints since 2007-08.

    Telstra recorded its best Complaints in Context result to

    date with 6 complaints per 10,000 SIO.

    The Complaints in Context report is a quarterly release

    jointly published by the TIO and Communications Alliance. The April-June report

    can be found on the TIO website, and Communications Alliance website.

    Harvey Norman Surges 8.8% On Improved Retail Trading Data

    A $20K tax incentive for small business initiated by the Coalition Federal Government coupled with a boom in house sales has paid off for Australian retailers especially for consumer electronics and appliance retailers.

    New Data from the Australian Bureau of Statistics (ABS) showed that retail turnover increased by 0.7% in seasonally adjusted terms in June following a 0.4% rise in May. 

    The June figure is nearly double the 0.4% increase that economists polled on Bloomberg were expecting.

    Electrical and furniture retailer Harvey Norman Holdings Limited surged 8.8% to a more than one-month high of $4.84 – making it the best performer on the ASX 200 index in early afternoon trade.

    In the latest ABS data it was household goods retailing that proved to be the brightest star with the category growing by 2.2% as New South Wales led the states with 1% growth. 

    Next week JB Hi Fi is tipped to announce an increase in sales despite some CE categories slowing. 

    Embattled department store Myer also went up earlier today jumping 3.2% to $1.30 even though the ABS data showed that department store sales slipped 0.1% for the month.

    Motley Fool said that “The fall wasn’t as bad as some had anticipated but that is hardly a catalyst for a re-rating in the stock as management is undertaking a rejuvenation program”. 

    This year JB Hi-Fi Limited is 20% up since January 2015 and is now trading at $19.46 with a further rise tipped next week.

    Another CE retailer that analysts are watching is Dick Smith who yesterday confirmed what ChannelNews has been saying for several months in that they are finally expanding into consumer appliances, setting it up for head-to-head competition with JB Hi-Fi Limited and Harvey Norman as well as the The Good Guys and Bing Lee. 

    Dick Smith CEO told ChannelNews that he is looking to grab 10% market share in the $1.7 billion small appliances market which some analysts claim is an optimistic target as he has to firstly get access to several brands and then compete head on with the likes of Harvey Norman who recently expanded their range of small appliances. 

    The move comes as several appliance retailer admitted to ChannelNews that it was Aldi who was stripping appliance market share away from them a move which several said was “a real threat”. 

    CEO Nick Abboud told Fairfax media, “Just after Christmas a lot of suppliers will bring in products with apps – you won’t have to press buttons on those products, you’ll operate them through your  phone or tablet or even get them on your smart TV .  as we evolve that’s where electronics will end up.”

    Dick Smith will establish a small appliance store under the ConnectedHome brand inside Dick Smith retail stores, which will sell toasters, kettles and coffee machines. The retailer plans to install ConnectedHome into 100 stores over the next five months, and will expand its range of personal care devices such as shavers and electric toothbrushes, and introduce new products such as blenders, vacuum cleaners, irons, fans and heaters.

    JB Hi-Fi successfully branched into white goods a few years ago, so there doesn’t seem to be much reason why Dick Smith can’t do the same thing said Motley Fool. 

    They added “Yes, there will be more competition, but that will generally impact much more on the highest cost retailers, rather than those with ultra-low costs of doing business”