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; } } Oonagh Reidy, Author at Smart Office - Page 88 of 116

    Smart Office

    OZ Digital Auction: Going, Going …(Just Not Yet)

    Minister for Broadband confirms auction of spectrum for ‘digital dividend’ will now not take place til April 2013


    Click to enlarge

    Communications Minister Senator Stephen Conroy,  confirmed the auction of spectrum between the telco will now not happen until next year.

    The auction for the “digital dividend” could see major telcos coughing up to $4 billion in total.

    The date was decided following advice of the Australian Communications and Media Authority (ACMA), Conroy said.

    “The ACMA has advised that an April 2013 auction would maximise product certainty for bidders prior to the auction and provide new licensees with adequate lead time to plan and deploy networks before their licenses commence.”

    Telcos including Optus, Telstra and Vodafone are all set to fight it out for spectrum in the 700 MHz band as well as spectrum in the 2.5 GHz band and will help telcos develop new 4G and other next gen mobile services.

    Telstra may pay up to $1.3 billion while Optus is thought to pay its massive spectrum bill in stages.

    Minister Conroy has also placed a competition limit of 2×20 MHz which will apply to spectrum in the 700 MHz band and a limit of 2×40 MHz to spectrum in the 2.5 GHz band and no entity will be permitted to purchase more than this limit for each band.

    The limits are designed to ensure there is a choice of providers in the mobile sector for the advanced services that will be provided over the digital dividend spectrum.

    The new spectrum being auctioned become available mainly as a result of the switch to digital-only television broadcasting by the end of 2013.

     

    A number of digital TV services will then need to be moved to new channels (or restacked) before the 700 MHz digital dividend spectrum can be made available to successful bidders.

    “The sale of this spectrum is a unique opportunity to pave the way for next generation mobile broadband services in Australia, such as 4G mobile services,” Senator Conroy said.

    It’s On: Internode ADSL2+ Hits Darwin $29

    Look out Telstra, Internode has turned its high speed ADSL2+ broadband on in Darwin and is ready to go.

    iiNet owned Internode are invading regional Darwin and spread its high speed plans throughout the land, for the first time.

    This comes after Perth based iiNet announced DSLAM (Digital Subscriber Line Access Multiplexer) network upgrades installed at four metropolitan telephone exchanges at Casuarina, Nightcliff, Palmerston and Darwin, Tuesday, and also plans to expand its services to regional Queensland.

    iiNet plans to connect 23,500 new users in Darwin alone and another 15,000 in QLD.

    Internode are flogging their popular ‘Easy Broadband’ ADSL2+ service with option of 30 GB – 1 TB data quota from $29.95- $119 per month, when purchased as a bundle with a Nodeline telephone line.

    Read: iiNet Invades: Darwin, QLD Next

    The telco are also offering a range of ‘Easy Naked’ standalone broadband plans.

    “At long last, we are delighted to offer our customers in Darwin access to the full range of Internode’s great broadband plans,” declared Internode managing director Simon Hackett.

    Internode Easy Broadband and Naked plans would deliver “significant benefits” to Darwin’s local community, he added.

    “It’s about greater choice.”

     

    Internode’s new Darwin DSLAMs connect to its national and international network through backhaul connections provided by NextGen Networks as part of the Federal Government’s Regional Backbone Blackspots Program.

    Internode also has its own DSLAM installed at Alice Springs.

    For more details about Internode broadband plans available, click here.

    Optus ‘Resilient’ Despite 5% Profit Slump, Losing 85K Customers

    Telco No.2 remains bullish as reports what it says are “resilient” results in the face of “intense” competition.

    Optus financial highlights for the current quarter to the end of June include earnings before tax (EBITDA) up 1% to $560 million, and underlying net profit A$174 million – a 3% jump. 

    However, this was before exceptional items worth $18m, “ex-gratia payment” arising from workforce cutbacks.

    Total net profit after tax fell by 5% year-on-year to $161m compared to $170m the same quarter a year ago. 

    SingTel owned telco’s mobile business did well with total revenue rising 5% to $1.49 billion. 

    “Mobile growth was underpinned by continued postpaid customer growth, with 113,000 postpaid net additions this quarter,” it said.  There are now 4.89m Optus post paid customers. 

    This was on the back of several major smartphone launches including the Samsung Galaxy S II and the release of the white iPhone. 
    The number of 3G data customers also increased to 5.29 million, a 4% increase from a quarter ago, pushing total mobile base to over 9m.  
    However, the number of pre pay users slumped by 85,000 – meaning total net gain were 28,000 users. 
    This compares to biggest rival Telstra, who netted a mass of new customers (1.6m in last year) and now has 43% total market share. 
    “Australia has a clear No.1 mobile market operator – Telstra,” Nicole McCormick, Ovum’s senior analyst for Telco strategy, said, following the results. 
    However, Optus service and equipment businesses also fared well – revenue grew 3% and 15% respectively while EBITDA was said to be “stable.”

    Average revenue per user was lower, however, at A$45, reflecting what the telco says was “a higher mix of wireless broadband and increased value inclusions on selected plans.”

    Operating revenue overall increased 3% to $2.31 billion. This growth spurt was thanks to “continuing cost management” from all segments, which has included outsourcing and slashing management jobs, Optus said yesterday. 

    “Against the backdrop of a highly competitive Australian telecommunications market with competitors discounting prices and sacrificing profits, Optus delivered resilient results,” Paul O?Sullivan, Optus Chief Executive said. 

     
    “We remain focused on delivering profitable growth in a rapidly changing market and driving differentiation through innovative digital services and products.” 


    The Singapore owned telco cited Smart Safe back up mobile service and its “landmark” Optus TV Now mobile service and OfficeApps as helping it stay in the black. 

    In its ‘Business and Wholesale’ sector, revenue was “stable” with growth in ICT and managed services and satellite revenue. EBITDA increased by 1 per cent, with stable margins.

    It also cited some major contract boons including a four-year deal with Sydney Water, to deliver fixed, mobile services worth $30m as well as a 5 year “supply managed services for NBN Co Interim Satellite Service” worth around $200m.
      
    In consumer and SMB fixed line services, on-net revenue excluding Pay TV was stable, although on-net revenue dropped  2%.  EBITDA grew 5 per cent to A$58 million, with margin expansion of 2%.   
    The improvement was driven by higher on-net revenue mix and yield management initiatives.  

     

    SingTel Group revenue rose 7% to $4.61 billion with 2% growth in earnings in the quarter although profits fell to $916m – a 2.9% drop. 

    Push Controls Qld, WA Sales

    Melbourne-based home automation developer appoints new interstate sales agents in Qld and WA.


    Click to enlarge

    Push’s new sales agents are Audio Industries in Queensland and PCA Marketing in Western Australia, it announced today.

    To keep up with popular demand, Push has appointed interstate sales agents to act as the first point of contact for its dealers.

    “With new additions slated for our product range and the growth of our dealer base in each state, the next logical step for us was to partner with local sales agentsand distributors,” said Push Controls managing director, Ben Green.

    “After meeting with several potential distributors in Queensland and Western Australia we believe we have found the right companies to promote Push Controls.”

    The appointment of the new sales agents comes at a time of significant growth for Push Controls, with the successful launch of Push Lite and several new products set for release in coming months, including the “game-changing” PC1 automation control processor.

    The new agents will represent Push Controls and assist new and existing dealers, giving them even better support through local channels.

    “After meeting with Laurie Douglas and his team from Audio Industries it was immediately clear thatnot only are they experienced and dedicated, but they share the same passion for the industry asus,” said Green.

     

    “Our meeting with PCA Marketing in WA was of a similar nature. Peter Cutts and his team are wellknown for their technical knowledge and previous experience with Nevo remote controls.”

    Audio Industries owner Laurie Douglas, said “we spent a lot of time looking for a qualitycontrol system that would complement our existing product ranges from Kordz, James Loudspeakers and Napa Sound Labs.”

    Since its launch in 2010, Push Controls has sought to become an important player in the Australasian AV control industry.

    E-Commerce 2015: What’s Coming?

    By 2015 there will be an estimated 3.4bn people surfing the web.

    By 2015 there will be 3.4bn people surfing the web.

    Thats a lot eyeballs.

    Mobile traffic accounts for 10% of web traffic currently and could be 50% by 2015.

     But “we have only seen the tip of the iceberg for this technology” in terms of availability, says Jake Hird, eConsultancy’s Director of Research, speaking at Online Retailer conference in Sydney this week.

    So what will the retail and marketing environment be like in 2015 and what will be the relevant technolgies?

    QR codes, although big news at the moment, won’t be here in five years, Hind believes.

    He reckons Near Field Communications (NFC) – the touch and tap payments technology – is the way mobile shopping is going.

    NFC will be widely used by consumers in the future, eConsultancy predicts.

    Facial recognition (FR) technology, where a user’s identity will be immediately recognised when they visit a particular site, will also be massive, and Facebook’s recent purchase of another FR technology startup supports this thesis, Hird predicts.

    The ‘connected TV’ will also play a large part in the retail environment of tomorrow, with product placement becoming more interactive in the age of Smart TVs with touch and Internet capabilities.

    There will be many opportunities in this space, eConsultancy believes. LG’s newly announced ad platform for Smart TVs in OZ, called smartclip, pay heed to this.

    The eConsultants are also predicting the “Internet of Things” where a multitude of devices will become web connected – for example showers are currently being fitted out with touchscreens – so it could be a case of whats not connected to the web in the future.

    Multiple touch points “will turn into thousands” and there will be more accessibility and inter-connectivity for consumers both in-store but also in other sites, says Hird.

    But there will be also more complexity as the number of touchpoints increases, he adds. Hird cites retailer strategies in the UK like John Lewis who have installed kiosks in locations allowing customers to simply browse in its online store.

    Many predict some major High Street retailers will be closed in five years he adds, but whether this becomes a reality has yet to be seen.

    The ecommerce guru cites the words of Google Chairman Eric Schmidt: “if you don’t have a mobile strategy you don’t have a future strategy. “

     

    And speaking of strategy, online and offline are now converging into one and no longer separate entities. “Data is the key for marketers” and ads will be more targeted than ever, Hird predicts.

    But he also cites EU regulations on cookies which could put a spanner in the works for retailers looking to tracks user buying patterns.

    “We’re in an experience age but moving to utilitarian age with real time interaction on devices. There will be “multiple digital platforms and it’s very difficult to know where these will be” – in the home, retail store etc.

    Instead of the 4Ps of marketing (product, price promotion, place), retailers will be thinking of the 4 Es – experience, everyplace, exchange and evangelism.

    But one thing is for certain, the future of retailing will be lead by technology, says Hird. “Steering the digital ship will be done through technology.” 

    NBN Fixed Wireless Blitz SA

    NBN Co announce fixed wireless broadband for 11 locations in South Australia.

    The company building the National Broadband Network, NBN Co,  has unveiled the areas where planned proposals will be lodged to deliver high-speed fixed wireless broadband.

    NBN’s fixed wireless network provides ISPs with wholesale access speeds of up to 12Mbps, with plans for higher speeds in the future.

    These are the first areas of regional SA to be slected for NBN wireless services.

    Over the coming months, NBN Co and its design and construction partners will work with local governments to identify appropriate locations for fixed wireless network infrastructure in parts of Mid Murraylands, South East, Far South East and Kangaroo Island.

    However, similar design plans, which ran into trouble among local authorities in neighbouring states, are “subject to final planning and other approvals,” says NBN Co’s Community Account Manager, Chris Gregory.

    Planned facilities will to be switched on in stages from mid 2014 in locations include:.

    · Coorong District Council

    · District Council of Grant

    · District Council of Yankalilla

    · Kangaroo Island Council

    · Karoonda East Murray Council

    · Mid Murray Council

    · Murray Bridge Council

    · Naracoorte Lucindale Council

    · Tatiara District Council· Wattle Range Council

    · West Wimmera Shire Council (Victoria)

     

    “This announcement is tremendous news for these regions, many of which have little or no access to high-speed broadband, or those confined to a limited service, such as dial-up or broadband over the mobile network,” Mr Gregory said.

    “Fixed wireless aims to deliver speeds and services that city people take for granted.”

    Unlike a mobile wireless service offered by telcos, where speeds can be affected by the number of people moving into and out of the area, NBN Co’s fixed wireless network is engineered to deliver services to a fixed number of premises within a coverage area.

    “For decades, rural and regional Australia has been left behind when it comes to telecommunications,” said Gregory.

    Aastra Hit SMB Ground Running


    Click to enlarge
    Aastra 470

    So a user could be abroad but still be contactable as the call will be re-routed to another connected device, which also cuts international roaming costs thanks to Aastra Mobile Client, which provides least cost routing.

    Remote workers’ calls are routed over the IP network meaning out-of-office workers have same functionality as in- office colleagues.

    The solution is also is simple and intuitive to use, say Aastra, and new 400 supports all smartphones including Apple’s iPhone, BlackBerry, Nokia’s Symbian and Google’s Android.

    The Aastra 400 system offers choice of either IP, SIP or digital desktop handsets and the entire solution can be tailored to suit clients needs.

    Flexibility is also the order of the day with this system, and can be bought outright or leased on rental agreement.

    Designed in Germany, the IP system is also is a fully open standard.

    The 400 series combines fully unified communications and collaboration (UCC) applications, mobility solutions and devices and help employees be contactable at all times throughout the day across all platforms with one number access, Interactive Voice Response (IVR) and email notification of voicemail messages. 

    It also boosts productivity with group voicemail boxes, instant messaging (IM), presence management and conference call capabilities.

    Basic handsets costs as little as $100, although the high end IP handsets will cost more.

    For multi-site networking, up to 40 sites can be linked to support a maximum of 600 users.  “This feature alone provides enormous potential for a business to create major cost savings in telephony” says Tony Warhurst, Aastra Australia Director of Sales and Marketing.

    Designed for an integrated voice and data network environment, it automatically configures to the network and maintenance can be performed via a web browser interface allowing remote access.

    The Aastra 400 also has strong networking capabilities, important for multi-site organisations like estate agents.

     

    The 400 series comprises three communication servers – Aastra 415 for four to twelve users; Aastra 430 for up to 50 users; Aastra 470 for up to 400 users. 

    All three servers are IP appliances which support legacy connections so SMBs can migrate to IP at their own pace. With the Aastra 400, all types of trunk and extension technology are supported.

    Launched in Europe earlier this year it has already received nod’s from communications industry including ‘Editor’s Choice’ Award from Business Info – a widely respected UK title.

    “The business customer is at the heart of everything we do at Aastra. The Aastra 400 series is a flexible and scalable, future proof solution,” adds Warhurst.

    Down, Down; Tech Prices Crash 6.3%

    Prices are a-falling: prices of PC’s, AV technology fell a whopping 6.3% this year, new ABS stats show.


    Click to enlarge
    Prices for tech goodies like tablets are now cheaper than ever.

    This massive drop in tech prices defied the slight overall 0.1% rise in consumer prices in the first three months of the year, revealed by the ABS Consumer Price Index (CPI) today.

    This slight inflationary rise compared with no change in the December quarter 2011, according to Australia Bureau Statistics.

    However, the headline CPI is now 1.6% through the year to March, compared with a 3.1% rise last quarter, meaning an interest rate rise could be on the cards for the Reserve Bank next week.

    The most significant price falls were for fruit, which fell a whopping 30% after last year floods pushed prices sky high, followed by ‘audio, visual and computing equipment’ which slumped a massive 6.3%,

    Price deflation is something which major retailers including Harvey Norman and Dick Smith have been crying about for some time now, on the back of online competiton pushing prices downwards.

    The ABS’ computer price index measures the price change of the same PC from month to month. “Any difference in price observed between one PC model and another is treated as a quality change as computing power and features improve over time,” according to the Index.

    However, since it has become clear Aussies have been overcharged for IT and tech goods for years, this price deflation means we may eventually align with the average prices the rest of the developed world pays.

    Telsyte analyst Sam Yip says Aussies are being overcharged on technology across the board.

    “Generally, the sentiment is that Australians are overcharged on everything from software right through to hardware,” he told SmartHouse.

    Read: How OZ Gets Screwed On IT Prices

    Resellers and developers are being “opportunistic” as they realise Aussies will pay higher prices, despite the cost of delivery of digital product being similar around the globe.

    But not for much longer – Yip predicts prices of technology will fall over the next 24 months, saying the price drop will be a “natural progression” for retailers in a bid to stay competitive in an online world full of bargain hungry  consumers.

    Discount sites popping up online are driving this price deflation, he added, saying recent research on 18 categories of goods sold online indicated discounts were introduced in all categories examined.

    However, the price discrepancies “will be milked for what its worth for the time being,” Yip warns.

     

    And just yesterday, SmartHouse reported on the exorbitant cost of IT software compared to US and UK with Labor MP Ed Husic calling on the government to investigate why Aussies are getting screwed over on software RRPs, claiming we are charged up to 80% more than US or UK consumers.

    International travel and accommodation (-4.8%), furniture (-6.0%), and domestic holiday (-2.0%) all fell in price this year.

    However, the major price rises included: pharmaceutical products (+14.1%), secondary education (+7.7%), automotive fuel (+2.5%), medical and hospital services (+2.1%), tertiary education (+4.7%) and rent (+1%).

    Its Baack: Click Frenzy ‘eCarnival’ Hits 2013

    After last years online fiasco, Click Frenzy is back, promising it has mended its way after last Novembers farce.
    Click Frenzy was OZ’s first 24-hour Internet sales event with 170 retailers taking part including Myer and Dick Smith, which led to multiple retailer servers and Click Frenzy’s own site crashing under the weight of 2.6 million consumers looking for e-deals.

    But now its rolling out a full calendar of events including the Mother’s Day Tuesday, April 23, Father’s Day plus End-of-Financial-Year and a pre Christmas ‘eCarrnival’ and Click Frenzy in November which will remain the staple event, Grant Arnott Click Frenzy co-founder said on Power Retail today.

    Mr Click Frenzy said improvements made to its systems to ensure the mass web crash won’t happen again.

    “We’ve listened, and we have learnt ” Arnott said, promising none of the online fiasco of last year, where consumers were unable to access the Click Frenzy and retailer’s sites for hours, despite having pre-registered, leading to an outcry on Twitter, Facebook.

    Even the ACCC admited they were aware of the fiasco.

    Despite this, Click Frenzy is definitely returning in 2013, as well as an eCarnivale which will hit Internet town the third week in November starting with the 24-hour mega-sale from 7pm Tuesday 19 with “all merchants on display.”

    The eCarnivale event continues for the rest of that week, with breakout events for home, entertainment and other categories.

    To help moderate the traffic, subscribers will be given early access to Click Frenzy events whilst for the public, the shopping site goes live at 7pm for the start of each event.

    Read: After Click Frenzy …. Comes The Big Q

    “Due to the outage and the subsequent controversy via social media, there’s a perception that Click Frenzy failed,” Arnott said.

    “Its popularity exceeded our wildest forecasts, and the site was down for the first 15% of the 24 hour period, which caused a major stir across media and social media.”

    Read: Click Frenzy: What The Retailers Say

    “There are still plenty of critics, but we’ve been buoyed by the overwhelming industry support for Click Frenzy, and we’re determined to deliver.”

    The co-founder of the sale event insisted there’s been “great enthusiasm” from consumers  and “we’ve had an incredible response from existing and new retailers on our Click Frenzy database for the Mother’s Day campaign and subsequent events.”

    However, the organisation was not available for comment when contacted by CN.

    If you would like further information on participating in Click Frenzy 2013, email info@clickfrenzy.com.au or call 03 9585 9869.