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

    Smart Office

    Senior Mobile Exec Quits Telstra

    EXCLUSIVE: Ross Fielding the Executive Director Mobility Products & Device Management at Telstra has quite and will leave the Company this week after 23 years working for Telstra.

    No explanation has been given by Telstra for Fielding’s departure who was a senior executive in the mobility division which in the last financial year grew 5.9%. 

    In his role at Telstra Fielding was responsible for product strategy, planning, development and lifecycle management across the mobile division. 
    Often the front executive for Telstra when vendors were launching new phones Fielding is tipped to be taking on a new role in the phone industry however he is at this stage refusing to say where he is going.
    “I will be leaving Telstra on Thursday and at this stage I cannot say what I will be doing” he told ChannelNews. 
    Prior to his current role, Fielding was Executive Director Strategy, Planning & Operations – Telstra Product Management (TPM) and played a key role in the establishment and structuring of the overall TPM group from its inception.  
     In this role, Ross was accountable for establishing the overall Product Strategy and Roadmaps (for all Telstra product sets – fixed, mobility, broadband and IP based), the development and management of the group’s business plans, the management of the Devices function (strategy / ranging / procurement / distribution / life cycle management) and the overall operational management of the TPM group.

     
    Ross has been with Telstra for 23 years and has held executive positions in a variety of areas; Chief Information Officer – Consumer / Retail, teams both large and small.
    Prior to joining Telstra, Ross worked with Ericsson Australia.
    Ross has a Bachelor of Engineering (majoring in electronics / communications), is a member of the Australian Institute of Company Directors and sits on the board of m.net Corporation Limited and AMTA (Australian Mobile Telecommunication Association).

    Tech Retailers Profits Set To Be Hit By Rising Dollar

    Mass retailers JB Hi Fi, The Good Guys, Dick Smith and Harvey Norman are facing a fall in profits due to the strong Australian dollar claim senior executives.

    Among goods sets to be hit are PCs, flat screen TVs, Smartphones and the highly profitable accesories section.
    The retailers have seen prices on items such as flat-screen TVs plummet and is likely cause profits to plunge this year in the sector, The Australian Financial Review reports.
    Harvey Norman Ltd chairman Gerry Harvey and JB Hi-Fi Ltd chief executive Terry Smart told the newspaper that increased production in Asia and competition between suppliers had seen prices drop by up to 30 per cent.
    They said the rise in the dollar was more harmful for profit margins than lacklustre consumer confidence during the global financial crisis.
    “The GFC was bullshit – this is a lot worse now for people in this particular space,” Mr Harvey said.
    Mr Smart said his company was trying to stem the impact of price deflation by selling high volumes of products, but admitted that the increasing trade was not enough to offset plummeting profit margins.
    Analysts anticipate cautious earnings outlook comments at JB Hi-Fi’s annual meeting today and Harvey Norman’s next month, the AFR said.

    LG OZ Appoints New Marketing Director

    EXCLUSIVE: LG Australia has appointed a former Electrolux and Johnson and Johnson marketing executive as their new Marketing Director to replace the dumped David Brand.

    Nick Gibson, who quit Electrolux in March 2009 to start NP Gibson Consulting was prior to that, the Vice President Product Development Asia Pacific for Fabric Care, and Marketing Director Electrolux Australia. He worked there between June 2005 and March 2009.
    Prior to that he was Marketing Director at Johnson & Johnson Pacific where he worked for five years.
    The appointment follows a major shake up at LG Australia who is still facing a multimillion dollar fine following false marketing claims that were exposed by Choice Australia. 


    Click to enlarge
    Nick Gibson Centre at Electrolux function.
    Currently under investigation by the Australian Competition and Consumer Commission, LG Australia is facing further fines for misleading consumers after consumer watchdog Choice claimed that LG Electronics haD duped consumers by using an illegal device within some fridges to make them appear more energy efficient.
    Earlier this year Choice, said that they had recently discovered a banned circumvention device within two LG fridges which overestimates the energy efficiency of the product. Choice reported the problem to the Australian Competition and Consumer Commission.
    Shortly afterwards LG Electronics in Korea moved to restructure their Australian operation. 
    During the past six months LG Australia has dumped several senior executives including the marketing manager of Audio Visual Consumer Products, the Marketing Manager of their mobile Division the overall Marketing Manager of LG Australia David Brand.

     
    Also dumped was the company’s Australian Managing Director, Daniel Shin. He was replaced by William Cho who previously was Managing Director of LG Canada.
    According to Brad Reed, a former Nokia Executive who was recently appointed Marketing Manager of Mobile at LG Australia, the company is set to invest significantly in marketing. “The primary objective is to get LG Australia to #1 in the consumer electronics market” said Reed last night when launch two new LG Android phones into the Australian Smartphone market.

    COMMENT: Will the Audit Of Samsung Australia Result In Big Changes?

    COMMENT: The decision to fly in a Samsung Corporation audit hit squad of 13 people to audit the Australian operations of Samsung Australia appears to be a drastic move by any count or for any company.

    This is not the first time that this squad has taken such drastic action when there have been allegations of corruption and non compliance with company procedures.
     They did it in China and Indonesia where, in both operations, they were able uncover irregularities which in some cases involved people who had been employed by Samsung Australia at one time or another.
    As former Democrats leader Don Chip once said ‘you always need something to keep the bastards honest’.
    The fact that the Samsung Corporation has taken such drastic action is a credit to the company as it demonstrates that they are prepared to act in the best interest of their staff, suppliers, distributors and retailers.  
    In today’s marketplace it’s critical for large fast growing companies like Samsung, whose Australian operation has grown to become the largest consumer electronics and appliance firm in Australia with revenues in excess of $1.6 Billion, are seen to have in place procedures that are open, fair and, above all, accountable.
    What we have done in writing on this story is demonstrate that a corporation is prepared to take action in the best interests of their brand and their reputation.
    In Australia, I have seen Samsung grow from a small company whose products were originally sold by distributors to a brand that, along the way, has faced some massive hurdles in getting to #1.
    It’s been obvious for some time that there were problems at Samsung despite their dramatic growth. During the past 24 months the company has lost several senior and experienced managers, people who had knowledge of the industries that Samsung compete in, as well as some excellent relationships with key buyers of their goods.

     
    To a company like Samsung this is a vital loss. Several times over the past 24 months we have been approached by both existing staff and those leaving the firm, and every time we were approached it was the same old stories, Korean management, hidden agendas etc, etc etc.
    We chose not to write on this subject, not because we were in fear of losing an advertising partner but because the claims lacked documentation or evidence to support the claims.
    However, when one gets tip after tip that that a hit squad of auditors have descended on a company as large as Samsung Australia, we as a media organisation have a responsibility to report the facts. Especially as these tips came from management still working within the firm as well as from those who quit Samsung out of sheer frustration.
    In the past we were told the same old story by several existing and departing employees who said “great company, liked working there” but we “can’t handle the Korean management”. “They are secretive, create divisions in the company and above all “don’t work as a team”. 
    We were told about private deals that managers were told to go nowhere near if they wanted to keep their job at Samsung. We have taped interviews from employee after employee, some of whom had used secret “dob” in lines to raise complaints about procedures and management. 
    Time and time again we heard the same story. We also heard similar stories about LG Australia and their Korean management issues.
    Today most of the people who quit Samsung are senior executives in major companies responsible for multimillion dollar budgets. They play key roles in growing the operations of competitors to Samsung. 
    What Samsung is doing via the use of an audit team is fixing a major problem that will result in Samsung Australia becoming a better operation.
    I, for one, believe that a subsidiary like Samsung Australia should be run by a local CEO who has a long history of success in a Western market like Australia. Panasonic, Sanyo and Sony are already doing this in Australia, and LG Australia, who after their own set of problems and record multimillion dollar fines, has moved in a Korean executive who speaks excellent English and has a track record of successfully running their Canadian operation.
    The ideal place for Korean management in a company like Samsung Australia is in CFO and operational and logistic roles where dialogue and reporting has to be kept up with overseas manufacturing and head office divisions.
    Samsung Australia is an Australian company and it needs an Australian face at the pointy end.

     
    In today’s market, the head of a major consumer electronics company needs to be able to communicate where his company is going and why. In the past, most of the CEOs of companies like Samsung, LG or Sony have not been able communicate a vision for their company because of poor English skills and a lack of Australian knowledge.
    Globally, Samsung has benefitted from the appointment of executives with Western market experience. Dr. David Steel is a classic example. Today he is head of strategic marketing for Samsung Electronics in one of their most important markets, North America. 
    Prior to joining Samsung, Dr. Steel worked at McKinsey & Co. and Argonne National Laboratory of the U.S. Department of Energy. Dr. Steel earned a Ph.D. in Physics from MIT, an MBA from the University of Chicago, and a BA in Physics from Oxford University.
    I first met David Steel when the Samsung consumer electronic division was a fraction of what they are today. Prior to him coming on board, Samsung advertising was created in Korea for use in markets like Australia, it was boring and did not reflect our culture or lifestyle. 
    Steele changed all that in his role as global marketing director and Samsung has blossomed to become a powerhouse in consumer electronics. 
    Steel has deep experience across several Samsung business units and possesses a breadth of expertise in many product categories. 

     
    He began his time at Samsung in the Global Strategy Group, where he executed strategic projects for numerous companies within the Samsung Group. From 2002-2007, he served as Vice President and head of marketing for the Digital Media business where he established the role of marketing within the $25bn business.
    Working closely with subsidiaries like Australia. He was able to strengthen the focus on key accounts, leading to sales growth of more than 300%, and develop leading capabilities in marketing communications and public relations. In 2007, he moved to the Mobile Communications Division, where he oversaw marketing strategy for the world’s number 2 maker of mobile phones.
    What has happened at Samsung is not a bad thing and the fact that we have exposed the problems in such an open way, goes a long way to Samsung’s credibility as a company.
    The consumer electronics media has a role to play in writing about both the good and the bad that happens in this industry, we are not here to be used as a party political broadcast machine by PR companies who simply want favourable exposure for their products and services.
    We write about an industry that is today a key part of society one that has migrated from niche technology publications and trade magazines to today being every day stories on TV and in mass media publications.
    I am sure that had we not exposed the events taking place at Samsung, the newly anointed Marketing Director Lambro Skropidis would not have issued a press release.
    The fact that we have the contacts and the experience to write such a story would have been useless without the conviction of both current amd past Samsung employees who went out of their way to contact us in coffee shop meetings, by phone or email from private accounts. 
    This speaks volumes to the extent of the frustration that people have been going through at Samsung Australia, who through one audit team swoop could become an even bigger and better company in Australia.
    If you would like to comment on this story please send your comments to dwr@4squaremedia.com

    A Bali Haven That Delivers Fast Internet For Free & A Really Cool Experience

    I’m sitting next to a pool in Bali tapping out a story on a new 7″ tablet that’s connected to the Internet via a very fast Wi Fi network. Welcome to a new look Bali resort that delivers IP connectivity that’s faster than a lot of big brand hotels in Australia, is modern by any hotel standard, while still delivering a great Bali experience.


    The Haven resort in Seminyak, Bali, is one of those rare technology gems where the architects seems to have got everything right including the hotels design and the discreet integration of a network which delivers fast Internet whether you’re sitting down by the pool with an iPad or using a HTC Smartphone to call home using Skype.


    Click to enlarge


    The best news about this one year old pioneering lifestyle resort which is situated in Seminyak, one of Bali’s most sought after suburbs, is that it is modern conveniently located close to the beach and local shopping, and wherever you go in the resort, fast internet access is always on tap at no additional cost.

    The Haven Resort which has been built from the ground up to cater for the modern traveller without compromising on the charm of what Bali is best known for offers a choice of rooms from top end villas with your own pool to suites and general rooms that have the latest LG flat panel TV’s and fast Internet access via a fibre network.  
    Opened a year ago this resort has managed to retain a unique look and feel in keeping with what Bali is all about, by using lots of timber and stone. Hidden behind the facades and local stonework is a network of wireless antenna’s which deliver a broadband service that is significantly faster than a lot of mainstream hotels that I have stayed in Australia.


    Click to enlarge


    When you step inside this resort from the hustle and bustle of Bali you feel that you have just stepped into a sanctuary that brings together outstanding contemporary design, tropical landscaping and all the services that you expect from a five star resort.

     


    The only difference is that this resort is available via Flight Centre for as low as $125 a night and for that, you get an excellent modern room with air conditioning, a 36″ flat panel TV, Internet access and rooms that just as good if not better than many 5 star hotels across the USA or Australia.


    Click to enlarge


    Having travelled to Bali for over 30 years I have stayed at a lot of properties from the Hyatt to the Inter Continental to The Four Seasons and the Haven in Seminyak is up there with all of them when it comes to service, price and the high quality of the rooms.

    Having access to fast Internet that is free in a place like Bali can save you hundreds of dollars. For this trip I took two tablets with me, the iPad and the new Telstra T Tab and my HTC Desire. With all of these devices I had access to Skype which allowed me to call home at no cost or little cost if I used the Skype phone service. I was also able to call my wife from around the hotel using Skype. 

    The Villas at the Haven offers seven villa residences of one and two bedrooms featuring private pools and split-level living. 

    All villas feature the latest amenities including LCD flat screen TVs, digital audio systems, queen size beds with high thread count linens and custom furnishings throughout. 


    Click to enlarge


    The villas also benefit from a concierge service, which endeavours to fulfil a diversity of requests and requirements. 

     


    The Haven also has 60 spacious suites offering a total of 90 bedrooms in one and two bedroom configurations. The earthy minimalist style suites overlook a trio of swimming pools ensconced in landscaped tropical gardens.

    The suites are fitted with the latest amenities including fully equipped kitchenettes, LCD flat screens with cable, digital audio systems, broadband cable Internet and oversize daybeds on the balconies.


    Click to enlarge


    For those who are food conscious this resort has an excellent dining room with a view of the pool at one end and a street view of high street Seminyak at the other. As for the food the choice and the pricing was excellent. If you don’t fancy hotel food within minutes of the resort is a stunning French restaurant run by a 3 star Michelin chef where a meal for three with a great bottle of French wine cost under $125, or you can walk across the road for a Greek meal that as good as any you will get in Greece. 
    There is also an abundance of excellent local restaurants serving Balinese foods.

    For those who like to stay in touch when away this is an excellent value for money resort that delivers the best of both worlds, fast Internet access and a resort that’s modern, well run and very Balinese.

        

    Was This The Worst PR Event Of The Year?

    COMMENT: I am two hours out from having attended a Western Digital press conference that had to be one of the worst press events held this year by a technology vendor. Ironically the event was for a storage company that appears to have a problem sourcing 128K USB Drives.


    The event for the launch of the WD Live Hub was so poorly run that two hours after the event, I have no digital media file or DVD with essential press information to write my story, for the simple reason that the PR company, GAP Marketing, only had hard copy print press releases which in 2010, when journalists are filing for online is a disgrace especially as the vendor was a storage Company.

    The PR company running the event couldn’t even find time to email a press release despite the event finishing two hours ago.

    Topping this off the function was held in a Surry Hills restaurant which was grossly inadequate for a launch of this type.

    Not only were journalists unable to see the main presentation screen but several were left standing because of a lack of seating.

    In today’s highly competitive PR environment we are seeing more and more press conferences being run at locations that are totally unsuitable. While a PR flunky may think it’s nice, the bottom line is that journalists are being invited in the hope that they will write a story about the product being launched.

    One would think that the best they could do is ensure that all journalists got a seat and a view of the screen.
    As a journalist, my job is to collect information and write a story that can be online in minutes. Often we file direct from an event or straight afterwards because of the competitive nature of the technology industry.

    Having owned and run the third largest PR company in Australia it appears that some golden rules of good PR are fast disappearing and one has to question why. 

    For example journalists often get given press releases after an event and not just before an event. I for one prefer to get a press release before an event so that I can absorb the content and then ask intelligent questions. 

    By now, I should have filed my Western Digital story online, but I am not prepared in this day and age to sit down and have to re type key technical data about what is basically a hard drive in a box from a hard copy press release, when in seconds I can cut and paste the same data from a digital file. 

     


    Last night at a Motorola press event journalists were given press images that were formatted as PDF files and not JPG images, which meant that journalists working to deadline had to firstly open up the PDF file, then save it as a JPG file, and then import it into a package like Adobe Fireworks for use online. This is time consuming and totally unnecessary.

    What is critical for most working technology journalists is a chair, a desk and good information. Holding press events at totally unsuitable locations does not work. Acer held one recently in a night club where journalists had to crouch on stools with over 50 percent of those who attended left standing in a room that was totally inadequate for a fashion show and Smartphone reveal.

    At the end of the day a location adds little if any value to a good story. What are important are good facts and an environment that is conducive to journalists being able to collect information and then turn it into a good story.

    Especially if all you are doing is pumping up a ‘me too’ product like a 1TB hard drive that has been tarted up and re labelled as a media centre.

    Is Microsoft Yesterday’s Technology Company?

    COMMENT: I purchased my first piece of Microsoft software 25 years ago in December. At the time Microsoft was a brand new company and I was running a brand new PR company that had several large enterprise computing clients.For 20 of those past 25 years, Microsoft has been on a roll, prosecuted and fined for their monopolistic practises. Microsoft has dominated in the desktop OS market, the Office applications market and in the server space with products like Exchange, SQL and a host of IT tools.

    Today the company is struggling on several fronts with CEO Steve Ballmer moving to sell over $1.7 Billion dollars worth of shares in the company this month.

    Their share of the browser market has slipped from over 90 percent to less than 45 percent as Google Chrome starts to take share away from Internet Explorer.

    In the mobile market the company has seen their share of the Smartphone market slide from over 80 percent to less than 5 percent. Now they are trying to fight back with Windows 7 Phone, up against a surging Google Android and a much sought after Apple iPhone offering.

    In the consumer market Microsoft has totally screwed up in Australia. Despite several promises the company has failed to deliver content such as movies and music to the desktop.

    Five years ago it was Microsoft not Google who had the software via their Media Centre offering to deliver a TV service. The only problem was that Microsoft failed to deliver an electronic program guide, or additional services similar to what their US customers were being offered.

    As a result it is Google with their Google TV offering that is attracting third party vendors like Sony and Logitech to partner with them.

    In the tablet market it is Google with their Android offering and Apple with their iPad that is grabbing consumer attention. Microsoft, who 12 months ago stood up at the CES show in Las Vegas with a tablet offering via HP, have still not delivered a tablet product and as for HP, they are moving onto their own WebOS offering with both a tablet and WebOS Smartphone set to be launched in Australia in 2011.

     

    Another big problem for Microsoft is cloud computing. While the company has sucked up sales of their Office range of software during the PC era several companies, including Google, are now offering businesses an alternative in the form of hosted applications.

    Last week Flight Centre said they were dumping their Microsoft products. Instead they are deploying Gmail to 13,000 employees worldwide, replacing Microsoft Outlook.

    Also dumping Microsoft products is real estate franchisor, Ray White, who is deploying a customer application available to 10,000 staff in its 1000 franchisees after trying unsuccessfully to use Microsoft .Net.

    Microsoft is not a very nice company. They bully and intimidate organisations, and their management blatantly lie, when confronted over issues such as when we exposed that 30,000 of their Xbox 360 gaming consoles had overheating problems.

    While Microsoft claim that they have billions invested in research and development one has to question why companies like Google, Apple and even brands like HTC are able to outperform Microsoft in the fast growing consumer market, and in the future, in the small medium business market.

    I remember one day when Microsoft ran out of press releases at the launch of a new Windows OS, when I asked for a USB or DVD with the press kit on, I was told that none were available.

    This is a company that tells major companies how to run their business. “We have the tools for just in time” performance they claim, yet despite this they fail internally to get a simple thing like a press release kit right.

    Even now Windows Explorer is fat, slow and buggy and Google is taking advantage of this with a significantly faster Chrome offering. In a few weeks time vendors will be offered a free version of a new Chrome OS to load onto notebooks, netbooks and PCs to replace Windows.

    At first many will bundle both but it will be interesting to see how long it takes before consumers and business demand a 100 percent Chrome offering as opposed to a Microsoft windows OS.

     

    The dominance of Windows has been Microsoft’s greatest strength for decades, but the operating system is now under attack from Linux.

    Microsoft saved Apple in 1997 with a $125 Million dollar handout. At the time Apple was considering a move to a suite of Linux based desktop applications developed by Corel. By giving Apple $125M they not only staved off Apple going into Chapter 11 they stopped Linux apps from getting onto the desktop.

    13 years later that is all about to change. Microsoft’s long running battle with Linux is eroding its market share on the server side, while Apple is making slow but steady progress in the desktop and laptop businesses.

    The situation is even worse on the mobile front, since Microsoft has never enjoyed the kind of dominance it has with PCs and servers in this market.

    Apple, Google and RIM are all cutting into the market, and the response of analysts and customers to Microsoft’s Windows Phone 7 mobile operating system has been tepid at best.

    Al Gillen, programme vice president of system software at IDC, told V3 recently: “A serious problem I see for Microsoft is that the emerging/growth markets that it has been cultivating for so long are where some of the most innovative uses of mobile technologies are occurring.

    “The bottom line is that, by the time those consumers are ‘ready’ for PCs, their attentions may have shifted elsewhere.”

    Best Buy Profits Crash OZ Web Site Coming

    Best Buy, the giant US consumer electronics group who are set to open up their web site to Australian consumers, has posted a 4.4 percent drop in profits to $217 million, down from the $227 million figure at the same time last year.

    As domestic same-store sales dropped 5 percent, due to declining sales of TVs and entertainment hardware and software, International revenue grew 3 percent to $3.2 billion for the same period. The revenue gain was driven primarily by the impact of new stores in the past 12 months and a 2.3 percent increase in comp-store sales.

    ChannelNews has been told that Best Buy will open up their online site with an Australian URL in 2011. This could result in Australians having access to goods at up to 40 percent less than what Harvey Norman, Officeworks and Dick Smith currently charge for the same goods.

    “While sales were lower than we expected during the quarter, I’m pleased with our strong store execution, solid gross margin expansion and efforts to control costs,” CEO Brian Dunn said.

    Best Buy said that domestic US revenue declined more than expected, driven primarily by larger-than-expected industry declines in key U.S. consumer electronics categories for the three months ending Oct. 31, as well as a decline in the company’s estimated domestic market share for such period.

    The domestic segment experienced a low-double-digit comp-store sales decline in TVs and entertainment hardware and software. Domestic comp-store sales decline in TVs was driven by a low-double digit decline in unit sales and a mid-single-digit price decline as the industry continued to experience softness during the quarter, Best Buy reported.

    The company’s gross profit rate for the fiscal third quarter was 25.1 percent of revenue, an increase of 60 basis points when compared with the third quarter of fiscal 2010. The domestic segment gross profit rate for the fiscal third quarter was 25 percent, compared with 24.1 percent for the prior-year period. The 90 basis point year-over-year increase in the gross profit rate was due primarily to continued growth in Best Buy Mobile and improved promotional effectiveness due to lower costs in financing programs and improved pricing strategies.