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

    Smart Office

    Kortek Take On Pana And Samsung In Pro AV Display Market

    Image Design Technology (IDT) has rolled out new Kortek high performance, low cost touch enabled LCD displays for the PRO AV market. They range in size from 32″ to 70″. The products have been launched as vendors like Samsung and Panasonic expand their presence in the Pro AV display market.Image Design Technology (IDT) has rolled out new Kortek high performance, low cost touch enabled LCD displays for the PRO AV market. They range in size from 32″ to 70″. The products have been launched as vendors like Samsung and Panasonic expand their presence in the Pro AV display market.
    At the recent CEDIA event on the Gold Coast three of the biggest stands AMX, Samsung and Panasonic displayed product for the Pro AV market not the CEDIA home market.
    The new Kortek touch enabled LCD Displays have high brightness and high resolution that deliver an eye-catching touch enabled display that are described as being “reasonably priced”. They are ideal say IDT for gaming, hospitality, education, retail, corporate, government and several other applications.
    Equipped with sensitive and reliable touch sensors, Kortek’s interactive displays enable the panels to be used in many different applications such as Information Displays, Way Finders Interactive Whiteboards and as a Touch Interface to other devices.
    The panels are available in a range of sizes including 32″, 40″, 46″ 52″, 57″ and 70″.
    Kortek panels utilise optical imaging touch technology, which provides a number of advantages to the touch screen user including – Zero overlay or films – 100% light transmission – High Resolution – Plug & Play – HID compliant (no drivers) – Easy Calibration, zero drift technology – Highly Scaleable – Versatile Durable glass front,-  scratch resistant – Fully ADA compliant.
    Gerry Wilkins, Managing Director at IDT, says that the introduction of Kortek panels to the Australian and New Zealand markets represent a significant step forward for users of this technology.
    “Kortek are a market leader worldwide in touch screen technology and provide high quality products, they are constantly engaged in new research and product development, and are totally committed to maintaining their leading position in the industrial touch screen market. We are very pleased to be working with them”
    “Kortek’s touch enabled LCD panels are a great addition to the IDT product range and fulfill a demand from our resellers for cost effective, large format LCD panels.”

    Tens Of Thousands Of Australian Adulterers Face Being Exposed After Hack

    Tens of Thousands of Australian adulterers are waking up today to discover that the confidential details they gave to infidelity website AshleyMadison.com have been hacked.

    Overnight it was revealed that digital extortionists are holding the sexual profiles of potentially 37 million adulterer’s hostage including tens of thousands of Australians. 

    On the front page of their Australian site they say ‘As seen on A Current Affair, Sydney Morning, Herald, Kerrie-Anne, Herald Sun, and The Australian.
    Ashley Madison is the world’s leading married dating service for discreet encounters

    According to Company sources the UK based website has over one million registered subscribers in Australia. 30% of those are believed to be females. 

    With a slogan like “Life Is Short. Have an Affair” and 37 million users, observers are claiming that it was only time before the web site was hacked.

    The culprits are calling themselves “the Impact Team” and say that if Avid Life Media, which owns Ashley Madison, doesn’t take the site down, they’ll leak all of the data they collected on the service’s servers.

    The Impact Team hackers assert that Ashley Madison’s “Full Delete” feature, which claims to remove all identifying data from company servers for $19, doesn’t actually work. 

    Krebs reports that the Impact Team wrote in a manifesto, “Full Delete netted ALM $1.7mm in revenue in 2014. It’s also a complete lie. 

    Users almost always pay with credit card; their purchase details are not removed as promised, and include real name and address, which is of course the most important information the users want removed.”

    Not that the hackers are exactly on the side of Ashley Madison’s users. “Too bad for those men, they’re cheating dirtbags and deserve no such discretion,” the group wrote. 

    In a statement, Avid Life Media said:

    We apologize for this unprovoked and criminal intrusion into our customers’ information. … We have always had the confidentiality of our customers’ information foremost in our minds, and have had stringent security measures in place … At this time, we have been able to secure our sites, and close the unauthorized access points.

    They added, we are working with law enforcement agencies, which are investigating this criminal act. Any and all parties responsible for this act of cyber-terrorism will be held responsible. 

    Bloomberg said ‘In this case, millions of people who were stepping out on their spouses-and hoping and praying today that the hackers don’t dump their philandering secrets online-are discovering a serious breakdown in their operational security: They used personal credit cards to pay for the service.

    Most people don’t think about it when they swipe a credit card or give the number to an online retailer, but the transaction actually reveals quite a bit about you.

     First and foremost: your name. In the AshleyMadison hack, those responsible are threatening to expose data that include payment information linked to painfully sensitive details from users’ profiles. 

    Those profiles contain the findings of an extensive survey given to new AshleyMadison users asking them to outline their reasons for being on the site and their most secret sexual fantasies.

    AshleyMadison boasts on its homepage that it is has more than 37.6 million anonymous members. It also touts that it is the leading dating service for “discreet” sexual encounters for married people. 

    Yet while it offers methods for paying fees anonymously, many people apparently didn’t use them. And despite the site’s assurances about privacy and discretion-including about how charges will show up on customers’ bills-it’s of little use if the data are linked on the backend in a way that hackers or malicious insiders can steal and leverage.

    After Dropping $4.4 Billion, Microsoft Is Having Another At Trying To Sell Windows 10 smartphones.

    After dropping $4.4 Billion, trying to prop up their struggling smartphone business by buying Nokia, an already dying phone brand, Microsoft is set to have another crack at trying to get traction in the mobile market with two new Windows 10 smartphones.

    This is despite the fact that consumers have overwhelmingly rejected a Windows OS based devise in favour of Android and Apple iOS devices.

    Currently Microsoft has less than 5% share of the smartphone market. 

    The new models that Microsoft is set to launch in Australia include the Lumia 950 and Lumia 950XL these models will ship with Windows 10 natively, they are tipped to be launch at the same time as Microsoft open their new store in Australia. 

    At Mobile World Congress in Barcelona Microsoft officials told ChannelNews that new Lumia models running Windows 10 would be ready for the Australian market by around “October”. 

    Juniper Research analyst Sam Smith said late last week that the new 950 device will probably run a 64-bit Snapdragon 808 processor, with the 950XL running  the more powerful 64-bit Snapdragon 810 processor. 
    “We expect a short-term uptake of Windows Phones, based on the new features that Windows 10 offers,” Smith told the US edition of Computerworld via an email. 

    “However, any increase in Windows phones overall will be slow, as it will take time for the app ecosystem to accelerate for developers to incorporate another OS on top of the Android/iOS duopoly.”

    Without that ecosystem, he added, “consumer adoption will be reluctant.” In other words, universal apps will “hasten any migration of consumers from iOS and Android,” he said. “But if the main body of Windows Phone are simply iOS/Android ports, there is little software-based incentive to switch. If universal apps for Windows on mobile have unique capabilities, then they will have stronger appeal.”

    The new devices will not support the new Continuum feature of Windows 10 which several analysts claim is “plain dumb”. 

    Continuum delivers the ability to plug a device into a keyboard and use it as you would a desktop.

    Not providing Continuum support in the Lumia 950 “would be a monumental misstep for the new OS, to have a flagship device unable to run one of the OS’s headline features,” Smith added.

    Chinese technology website IT Home, uses the codename “Talkman” for the 950 and “Cityman” for the 950 XL.

    The 950 according to manufacturing sources in Asia will have a 5.2-in. display, 3GB of memory, 32GB of internal storage with a microSD slot for storage expansion, a 3,000mAh removable battery and support for LTE wireless.

    The 950XL will have many of the same features as the 950, but with a larger 5.7-in display and 3,300mAh removable battery.
     
    There are also rumours that Microsoft will release a high-end smartphone with a front-facing LED flash.

    Overall, smartphone makers shipped 338 million smartphones globally to retailers in the second quarter, an increase of 16%. Apple continued its record-breaking streak with 47.5 million iPhone sales in the quarter. 

    Other Chinese smartphone makers generally saw shipments rise, with Huawei seeing 50% growth and Xiaomi increasing 33%. LG, based in South Korea, however, saw a 3% decline Computerworld reported recently. 

    Toshiba Launch Hot New Magnesium Business Notebook

    It looks hot, so hot that the all new Toshiba Satellite R630 has a brand air flow cooling system as well as, a brand new look that sets a new standard for slim stylish business notebooks.

    Designed from the ground up by Toshiba, who has been making notebooks for 25 years the new Satellite R630 with its 13.1 inch screen an nine hours battery life, is just one of three new notebooks set to be launched by Toshiba on Tuesday.

    Image TOSHIBA
    The other two models are the Toshiba AC100 and Libretto W100, two products which go against the design curve being worked to by other manufacturers like Dell, HP and Lenovo.
     Inside the ultra slim chassis casing of the Satellite R630 is Intel’s new Core “I” processors including the choice of either the i3, i5 or i7 processors. This makes the new Toshiba offering one of the most powerful of all notebooks available today.
    Key to the new design is the Toshiba patented Airflow Cooling Technology which stops the notebook from overheating due to the high speed of the Intel processors is such a slim casing.
    Toshiba claims that users should feel no notable heat at all when using the 30mm slim device.
    The chassis for the R630 is made from magnesium alloy which in drop tests from 70cm failed to break.  


    Click to enlarge
    Another key feature is the 1366 x 768 display screen which delivers a very bright screen without affecting the nine hour battery life.  
    It also has 3x USB2.0, 1x eSATA / USB combo port and a multi-card reader, VGA and HDMI ports.

    EXCLUSIVE:Former Best Buy Company Makes Offer For Dick Smith Tipped To Be Accepted

    The Five Star Chinese retail chain that was 75% owned by giant US consumer electronics retailer Best Buy, has put in a none binding bid to buy Australian retailer Dick Smith, the offer is believed to have been accepted by the receiver Ferrier Hodgson, who is waiting on acceptance by the banks who placed the mass retailer into administration.

    According to sources two offers, one from Indian conglomerate Tata, and another from Shenzhen MTC were rejected with the Jiangsu Five Star Appliance Co., Ltd offer, the only remaining viable bid. 

    All of the Companies that have made offers are believed to have approached the receiver after the formal bid process closed in January. 

    Five star was the only Company that put in what has been described as an “acceptable” offer. 

    Currently Five Star has several senior Chinese executives in Australia running due diligence along with their Australian CFO, at this stage their intent is to buy all of the Dick smith stores in Australia and New Zealand as well as the Move stores in Australia insiders said.  

    They then plan to close 45% of Dick Smith stores and any future stores will be “significantly larger” than current Dick Smith stores said a source. 

    The Company which was 75% owned by Best Buy are currently concerned about the financials supplied by Ferrier Hodgson and want to conduct their own due diligence audits. 

    ChannelNews has seen the financials that were supplied by Ferrier Hodgson.

    Five Star have made suggestions that the Company places an upfront bond and takes over the running of the retailer for several weeks before making a final payment.

    According to Dick Smith sources Five Star is believed to have approached several former managers in an effort to obtain and information while evaluating their possible employment in the new Company. 

    On June 8, 2006, Best Buy acquired a 75% interest in Five Star for US$184M which included a working capital injection of $122M and transaction costs, they sold their share in the business in 2014.

    What is not known at this stage is whether the stores will be branded Dick Smith, or Five Star.

    ChannelNews has been told that Best Buy has researched the Australian market on “a number of occasions” in the past. 

    Ben Cavender, a Shanghai-based analyst at China Market Research Group, said that when Best Buy decided to expand the Five Star Stores in China that they lagged behind competitors in the Chinese market.

    Wang Jian, co-founder of Five Star, was appointed global vice president for Best Buy at the time. We have also been told that despite the sale of shares in the Company by Best Buy the two Companies still have a close working relationship when it comes to the purchase of goods.

    Five Star, based in Nanjing, eastern Jiangsu province, has about 170 stores in seven provinces, employs more than 8,000 people and had sales of 24.7 billion yuan ($3.8 billion) in 2009, according to its website.

    ChannelNews understands that an announcement will be made on Thursday.

    HP OZ Reports Record $58M Loss After Being Hit With $3M For Misleading Consumers

    EXCLUSIVE: Hewlett Packard Australia, which was ordered by a Federal Court judge on Friday to pay a $3M fine and $200,000 in legal costs for misleading consumers, has reported a massive $58,238,000 loss for 2012.

    What is not known at this stage is whether HP’s consumer PC division, which the US company was looking to sell 12 months ago, was a significant contributor to the losses. IDC reported that their PC market fell over 20 percent last year in Australia.


    Also unknown is which Australian executives made the decision to engage in misleading consumers and whether there will be any personnel fallout from the Federal Court decision.


    According to documents filed with the Australian Securities & Investment Commission, HP Australia went from a profit in 2011 of $134M to a loss of $58M in 2012. A contributor to the loss was a $30M tax liability.


    Sale of goods revenue in 2012 slumped from $2.58 billion in 2011 to $2.18 billion.


    Also slumping was finance revenue, which fell from $6.3M to $5.16M.


    Despite losses and a fall in revenue, wages and salaries at the company rose by 80 percent, from $440M to $754M.


    Marketing costs also fell from $38M to $32M. It is not known how much of this expenditure was co-op dollars attributed to retailers selling HP products.


    The Federal Court  slapped Hewlett-Packard with the $3 million fine after a lengthy investigation by the Australian Competition & Consumer Commission, which concluded that HP management engaged in a “widespread and systemic” process that resulted in hundreds of consumers being misled by the Australian subsidiary.


    ACCC chairman Rod Sims said it was an important case. “The misconduct was widespread and systemic from a very large multinational firm.” 


    Rather than face court, where evidence would have been presented in an open court, HP Australia chose to negotiate a settlement. The court found that HP gave clients misleading advice on their consumer guarantee rights.


    According to ACCC sources, HP management involved in marketing HP PC products in Australia instructed HP call centre personnel to tell customers that products purchased online could only be returned to the company at its sole discretion, which was also how it determined product remedies.


    HP retailers have also been implicated in the companies’ misleading conduct.


    Consumers were told that they were required to have their product repaired multiple times, before being entitled to a replacement.


    Another false claim was that the warranty period for HP products was limited to a specified express warranty period.


    The ACCC initially instituted proceedings against HP on October 16, 2012.


    The year 2013 is not looking any better for HP, after research group IDC reported that the Australian PC market had declined 21 percent in the first quarter of 2013, compared to the same time last year.


    HP took the top slot in the Australian PC market in the first quarter of 2013, with a 19 percent share. 


    IDC is forecasting a further decline of 15 percent across the Australia and New Zealand markets.


    The announcement that HP had deliberately engaged in a process of misleading consumers and had been fined $3M after a series of discussions with the Australian Competition & Consumer Commission was made late on Friday afternoon.


    It is not known whether HP proposed a Friday afternoon announcement to the ACCC in an effort to minimise the PR fallout from the Federal Court decision. 


    Late on Friday, neither HP, nor their PR company, refused to supply a spokesperson for the company.


    Shortly after calling HP and PR company Burson Marsteller, SmartHouse got a spin statement from Biana Harkovskaya, Acting Media Relations Manager HP Enterprise Business, South Pacific.


    Receptionists at both HP and Burson Marsteller said they had been instructed to take the names of media personnel.

    Is The Franchise Model Broken? It Is Very Close To Being

    When Gerry Harvey went into battle recently for a 10 percent tax on overseas online purchases he set off an explosion which resulted in most Australians suddenly becoming aware that a lot of retailers are price gouging when one compares the overseas cost of a same brand product to that being sold in an Australian retail store.

    It also exposed the online weaknesses of several major retail groups such as Harvey Norman, The Good Guys, Betta Electrical and Retravision who primarily operate as franchisees.

    Online today is no longer about having a web site and a transaction engine. Online is about delivering choice, competitive pricing and above all having the engines in place that allow operators to deliver a first class customer experience.

    Email marketing and product tracking engines that allow consumers to track their purchase on a mobile or tablet are essential and so is bar code linked technology that allows consumers to access the latest information about a product or Company.

    A web site and a transaction engine was web 01, smart retailers in the US and Europe are now moving to web 03, with content openly available in several forms on multiple devices.

    Groups like JB Hi Fi who already has a web site and company-owned store operation can respond quickly to change. Franchise operators are not in the same position, as Harvey Norman has recently found out.

    To have to carve out an online margin for a franchisee and an operator like Harvey Norman or A Good Guys, instantly creates a layer of margin that non franchise operators don’t have.

    Then there is the issue as to who the franchisees are competing against. Is It Harvey Norman Vs The Good Guys or is it Harvey Norman Vs Amazon or E Bay store operators who are able to ship from overseas warehouses direct to an Australian consumer.

    These operators already have the technology that allows them to engage with consumers across several levels and several continents.

     

    They have the comparison pricing engines and the engines that prompt the sale of an accessory or extended warranty purchase, which in a lot of cases is supported by vendors who are moving to global warranty programs for their products.

    They also have the bar code technology that confirms instantly when a product has been delivered to a customer.

    Going forward, an Australian retailer is going to have to think global to compete locally.

    Working in their favour is the fact that Australians will pay a higher margin to buy locally. A recent Australian Institute research study revealed that Australians expect to pay a difference between 20 and 35 percent for an Australian delivered online product. Anything above that and the consumer will shop online.

    This presents big problems for a franchisee that has neither the technology nor the knowledge nor the funding to compete in an online environment where the benchmark is being set by a big global operator such as Amazon.

    By late 2012 we will see several Australia retail operators roll out overseas linked and operated web sites. Companies like Myer are already giving it a go. This will add a further level of competition as Myer battle with big brand players that are linked to organisations like Google and Amazon. 

    JB Hi Fi PC Sales Up As PC Market Tanks

    PC manufacturers in Australia are concerned that the new Microsoft 10 OS will not give them the lift that the industry needs after PC sales fell somewhere between 9.5% and 11% in the last quarter.

    Beating the downturn is JB Hi Fi who before their “quiet” period reported strong PC growth especially for Lenovo and Acer PC’s. 

    PC sales in the second quarter suffered their sharpest decline in two years, according to data released by research firms Gartner and IDC. 

    Manufacturers shipped 68.4 million PCs in the second quarter, a 9.5% drop from the same quarter in 2014, according to Gartner. Based on IDC’s data, the market saw a 11.8% year-over-year decline in the second quarter of 2015, with worldwide PC shipments of 66.1 million. IDC doesn’t count tablets in its figures.

    Analysts attributed three factors to this decline: companies reducing PC inventory in anticipation of Windows 10; weak exchange rates effectively making PCs more expensive; and unusually high PC sales last year because of Microsoft phasing out support for the Windows XP operating system.

    Over the weekend Toshiba teamed up with Harvey Norman to offer several cheap notebook deals with the big retailer using TV and catalogues to promote the notebooks due to poor Toshiba notebook sales across their network. 

    Lenovo maintains a strong lead in the latest numbers with a market share of nearly 20%, selling 13.5 million units in the second quarter, based on Gartner’s numbers. 

    But the Chinese company also experienced a 6.8% shipment decline over the same quarter last year – its first decline since the second quarter of 2013.

    At both Harvey Norman and JB Hi Fi Fi Lenovo has increased their share of the consumer notebook market.

    Apple  saw the biggest growth out of any PC vendor. Worldwide shipments for its Mac computers jumped 16.1% to 5.1 million units in the second quarter over the same quarter last year, according to IDC. The research firm said Apple may be benefitting from uncertainties surrounding Microsoft’s Windows 10 with tens of thousands of consumers switching to the iOS from Apple as opposed to a free Windows 10 upgrade. 

    Globally Acer PC shipments declined by 20% in the second quarter from the previous year, according to Gartner – and by 27%, according to IDC’s count.

    Many in the PC industry are expecting the impending release of Microsoft’s Windows 10 operating system to help spur sales in the second half of this year. But even though Windows 10 is coming out on July 29, analysts don’t expect the new operating system will be enough to bring growth back to the industry this year. Gartner expects shipments to decline 4.4% overall in 2015. Because Windows 10 is a free upgrade for existing Windows users, IDC expects many of these users will stick with their current PCs rather than buying a new one.

    Analysts expect the PC market to stabilize in 2016.

    New 10″ AMX Widescreen

    AMX is set to showcase its new 10″ widescreen offering at the up coming CEDIA Expo. Wireless enabled the screen fill a gap in the market where space is critical.

    AMX has expanded its range of Modero touch screen panel user interfaces with the introduction of a new 10″ widescreen format. The company claim that the Modero 10″ which will be on show at the CEDIA Expo in Surfers Paradise, has been introduced to satisfy the market’s need for an affordable, mid-sized touch panel, while offering substantial screen space for easy accommodation of graphics, icons and video windows.

    For those environments where space is critical, the Modero 10″ provides more visible screen area because of its 16 x 9 aspect ratio, but with a smaller overall footprint, providing greater visibility around the panel. The panel is available in wall mounted and table top configurations, which are offered with optional external pushbuttons and wireless capabilities. Wireless capabilities are provided by means of an 802.11b Wi-Fi Card, which allows users to benefit from the freedom of wireless control, ultimately eliminating unsightly cables to create an aesthetically pleasing environment. The Modero 10″ incorporates the latest G4 graphics, with outstanding high resolution, colour, contrast and brightness. Utilising colour active LCD technology and 800 x 480 resolution, graphics and video appear clear and sharp. The 10″ Modero features 18-bits per pixel of colour depth, 350+cd/m2 peak brightness and a 200:1 contrast ratio.

    Multi-media options include built-in speakers and microphone, a headphone connection and the ability to play back MP3 and WAV audio files. A photosensitive light detector on the front of the panel determines surrounding degrees of light and automatically regulates the brightness of the display. In common with all of the latest generation AMX touch panels, the Modero 10″ comes with 64MB of memory and compact flash, to support more graphically rich user interface designs and to retain valuable data in the event of unforeseen power loss. Commenting on the introduction of the new Modero 10″ panel, Rashid Skaf, President and Chief Operating Officer of AMX Corporation, said: “The AMX 10.4″ Colour LCD TiltScreen has enjoyed tremendous success for many years, but we felt a new panel of that size should be extended into the Modero Touch Panel family to incorporate the latest technological advancements offered only through a Modero. AMX is now able to offer a large selection of Modero Touch Panel sizes and features to integrate well into any environment. The full range of sizes includes 7″, 7.5″, 8.4″, 10″, 12″, 15″, and 17″. This enhanced and complete range fulfils customer demand and broadens our market base.”