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; } } Lilia Guan, Author at Smart Office - Page 3 of 8

    Smart Office

    InterVideo Sues Dell

    InterVideo announced its subsidiary InterVideo Digital Technology Corporation has filed a patent infringement lawsuit against Dell.

    InterVideo alleges that Dell’s software allows a DVD disk to automatically start playing a movie when a user inserts a disk into a computer running an InterVideo program. The DVD software maker alleges Dell had full and prior knowledge of the US Patent number ‘788 patent, entitled “Method and apparatus for integrating personal computer and electronic device functions”. It also claims, Dell’s infringement has been and continues to be “intentional and willful by importing, making, using, selling and/or offering for sale in the United States computer products, and components and peripherals thereof that embody the inventions of the ‘788 Patent”.

    It has also asked the California court to stop Dell from manufacturing, selling, offering to sell, use and import products which infringe on the ‘788 Patent. InterVideo also wants the court to order Dell to account for and pay it all damages, plus enhanced damages and attorney fees and costs.

    Dell spokesman Lionel Menchaca said Dell had no comments of the lawsuit, citing company policy against commenting on pending litigation.

    Earlier in 2004, InterVideo Digital Technology Corporation earlier filed two patent infringement lawsuits against Acer in the United States Federal Court for the Western District of Texas for infringement of the ‘788 patent, and in the Taiwan District Court of Taipei for infringement of various Taiwan patents relating to its InstantON(TM) technology. Both cases were resolved satisfactorily for InterVideo.

     

    Sony Tips Explosive Growth

    Sony Electronics is expecting an explosive growth in flat panel, plasma and LCD HDTVs over the holiday season.

    Sony Australia is tipping big growth for their recently launched Bravia LCD TV’s. However unlike its US counterparts the company is reluctant to talk about it.

    According to Graham Keogh, Product Manager, Plasma, LCD TV & Grand WEGA Consumer AV-IT Marketing said that Bravia sales have been “very good, in fact better than expected”. This is inline with sales of Sony products in the US market. He claimed that Sony Australia cannot comment on the percentage of sales made from these electronic products. He said, “We don’t release figures in Australia. What we talk about depends from country to country because of the non-disclosure agreements Sony has with manufacturers.”

    Stan Glasgow, president of consumer sales in the US for Sony said “Sony Electronics has had the best October in its history. Sales exploded with a bunch of new products. We have seen amazing strength in LCD and DVD camcorders.” He noted that the LCD Bravia line that was introduced two months ago “has taken over the number one position in the U.S. dollars in LCD, 38 percent.” Grand Wega rear projection has 50 percent share in dollar terms, Glasgow claimed.

    The popularity of Sony’s Bravia TV has reached across the Atlantic. Australian consumers are just as hungry for the product as their counterparts in the US.

    Keogh said, “It’s translating into sales and we have only started half of the marketing in the beginning of October and it’s selling three times better than we expected,” he said. “Quality is becoming a key driver, although pricing is always up there. People have been duped into buying low resolution plasmas, which doesn’t even resolve DVD. I believe LCDs will catch up to Plasma and within the next 6-12 months LCD will take over the Plasma quantity wise.”

    A survey by IDC conducted in October of what Australian consumers wanted for Christmas showed 70 percent of customers wanted a Bravia for Christmas. The survey was taken before Sony’s marketing campaign of the product. Only one month later retailers have stated not only are consumers asking about the product, it’s translating into sales.

    Phil Moujaes, General Manager Bing Lee couldn’t give figures on how many units the retail chain has shifted, however he did state the product was doing very well.

     “They’ve been accepted and the response has been very positive. Although I can’t give any figures, I can say the popularity has translated well into sales,” he said.

     

    New net home for mobiles

    Using a mobile to view websites on the Internet will get easier as they get a Net home of their own.

    Sites using the .mobi Top Level Domain will have to follow a style guide ensuring they are optimised for smartphones.

    The Internet Corporation for Assigned Names and Numbers (ICANN), which oversees the Internet’s addressing system, put its stamp of approval for the .mobi application on 11 July. This will make it easier for users of mobile devices such as handsets and handheld computers to find and browse for information via the WWW.

    mTLD Top Level Domain Ltd, will operate under the consensus policy set by ICANN for allocating and running domain name services for the .mobi domain. mTLD Top Level Domain Ltd is a joint venture between Hutchison 3, GSM Association, Ericsson, Microsoft, Nokia, Samsung Electronics, Syniverse Technologies, TIM, Telefonica Moviles, T-Mobile and Vodafone and was formed to create a registry service to the .mobi domain.

    “.mobi will indicate to consumer and enterprise users that the site they are visiting is optimised for the delivery of content and services to mobile devices. This quality assurance will attract users, stimulating Internet-based mobile data usage,” said Ameet Shah, Chairperson of the Board.

    Currently it’s estimated the first domain names will be available during the first half of 2006, starting with a 90 day sunrise period, allowing corporations with trademark validated names to secure a second-level .mobi domain. Generic second level domain names will be available on a first come, first served basis, after the sunrise period.

     

     

    A Growing Voice:VoIP

    In Australia, the overall VoIP market, spanning both software and hardware, is expected to grow 70 per cent this year. A little over 50 per cent of this growth is expected to be in the SMB market. According to Senior IDC Telecommunication analyst, Susana Vidal Little, the transition of what has been traditional enterprise technology into previously ignored SMB solutions, will lead to the growth.

    VoIP has already left the early-adopters stage and is entering an early-majority stage, especially for medium and large businesses. For the small and residential segment it’s still in the early stages of growth, but offerings like Engin and Freshtel will probably accelerate the adoption.
    “The main equipment vendors have been focusing on the medium/large enterprise and/or their prices are too high for small businesses. I don’t think the problem is quality but right price and size. There are vendors like Zultys and Mitel, who have products that address this segment (below 50 users),” says Vidal Little.

    Traditionally, systems integrator Dimension Data has focused on selling to the enterprise level government and corporate organisations. However, it recently set up a commercial sales and service delivery team with the objective to deliver on its plans to build and support solutions and services to a broader client base.

    Gerard Florian, chief technology officer at Dimensions Data says: “In terms of business IP telephony, it’s not just the technology and products that are critical – integration and support of the environment is also key to successful operation. Dimension Data developed its Managed IP Telephony Service to cater for the different needs of this broader client group, bringing guaranteed service levels and quality, at a foreseeable and fixed cost to organisations. We are also working closely with Cisco in tailoring and marketing our solutions to this broader market.”

    SMB Advantage

    The VoIP market can be broken down into several segments; first into services and equipment, equipment then being broken into enterprise and carrier equipment. There is another split between residential and business markets, each of which has vastly different needs.
    Large enterprises, when electing to deploy internet telephony, need to examine all aspects of the market; they will have to look at equipment, outsourcing the management of the system or retaining it in-house with the need to examine the stresses such technology will place on their network. However small to medium businesses have an advantage – easier installation and management of SMB VoIP products allows them to use cutting-edge technology once only available to large companies.

    According to Michael Boland, Cisco Systems Distinguished Engineer, VoIP call agents are leading the displacement of legacy TDM-based PABX and private voice trunking in enterprise customers, while SMB customers will have the choice between direct capital purchase VoIP systems or managed/hosted VoIP systems from a VoIP service provider. “Integrated VoIP solutions, at new price points, are now allowing these same enterprise features to be offered for SMB customers,” he says. “They are seeking reduced operations costs, increased services (via the integration of Voice with Data systems, e.g. Outlook integrated with their VoIP PABXs for conference calls and messaging) and business productivity that VoIP systems can offer over traditional systems.”

    Colin MacDougall, national sales manager for Mitel Networks, South Pacific agrees: “Many SMBs both globally and here in Australia are making the move towards IP telephony because making that transition for a smaller organisation can be less disruptive than it is for bigger organisations. In many cases the old infrastructure can be replaced quickly allowing the organisation to benefit from the new technologies and applications very quickly. With many smaller Mitel systems, we also sell Teleworker applications, effectively extending the office via broadband to either home or laptop in a hotel room,” he says.
    Mitel comes from a traditional voice background, but 75 per cent of its new system sales are IP-based. Traditional sales are forecast to fall over the next two years. “IP telephony will give SMB owners a platform and solution set that will allow them to differentiate themselves from other organisations. SMBs certainly need to think about the solutions and
    how they would be applicable to their businesses,” MacDougall warns.

    The Opportunity

    With all this going on in the market, small resellers need to be aware they now have the same opportunities and resources as large resellers to have highly trained engineers selling and deploying VoIP PBX’s.
    SMB resellers can quickly get up to speed on the deployment of small business VoIP, but must be able to educate customers on the importance of understanding how to use these tools to enhance their business.

    Tony Warhurst, director of business development Oceania for Zultys Technologies, says resellers from varied backgrounds no longer have to send away customers looking to deploy VoIP. “In the past you had large organisation retailers for an organisation like Cisco selling VoIP products, but they didn’t have the skill set to install or deploy the product. Large organisation resellers would have to send engineers to America and get them trained up to install IP PBX. But it’s all simplified now and more products have simplified interfaces that can be installed within minutes. This is where resellers can take advantage.

    “At Zultys we offer a three day course, which was once only available to engineers from large organisations with money and time to spend.
    We have resellers with varied backgrounds, some from a traditional PABX background, others from a software and server background and all have had customers make enquiries about VoIP,” he says.
    SMB resellers need to educate their customers and help them understand the cost savings of IP PBX (VoIP) not just toll bypass. Warhurst believes resellers need to “physically show customers how they can integrate many other applications that can increase productivity onto their phone system with IP PBX. They need to also show customers how it allows flexibility in where employees can work.”

    Warhurst told SmartOffice Reseller that although companies such as Cisco and Avaya have the branding advantage over lesser known names like Zultys, the company is rapidly building an installed base of reference sites. As it continues to win competitive pitches, resellers and customers are showing greater willingness to go with Zultys’ solutions based on price performance and feature set, especially in the SMB arena.

    Though Zultys has attracted start-up resellers eager to make a name for themselves in the VoIP business, it is resellers with an established IT clientele and database of existing customers that are getting the most wins. Warhurst adds that these resellers can now go to their existing customers and say, ‘you know we have looked after you before and we’d like to show you a solution that we think will work well for you.’

    IT Accessorie Bring In The $$$

    Computer accessories represent straight forward sells which brings in the money for resellers and keeps the customer complaints away.

    Computer hardware has seen resurgence of interest due to the broadband push. However resellers are still finding a hard time bringing in the profit because of the stockpile of cheap products in the marketplace. While they aren’t making much money from hardware products, resellers are seeing the dollars come in from the growing interest in IT accessories.

    Australia company Anyware is a niche player and national distributor of IT accessories, although it’s in the computer industry, it doesn’t don’t sell hard drives or CPUs. According to Garrison Huang, the company basically avoids major components, but all the other peripheral accessories i.e. Plantronic headphones, Mystic desktop cases, Pegasus’ Digital Pen, just to name a few for the past eight years.

    “Computer accessories are a growing business; we haven’t seen any decline because the peripheral market has a lot more potential than components business. Computers are more widely available and the recent growth in hardware sales is being fuelled by broadband. As people buy computers they also want try to see what they can do with their products,” said Huang.

    Although the company has signed a deal with Capital Security Solutions (which represents Norwegian based anti-virus, Norman), Huang feels this isn’t a huge deal for Anyware.

    “It signifies one thing and that’s our interest in tapping into the anti-virus market. I believe this is a growing market and represents a strong opportunity for us. However I think distributing products like Pegasus’ Pegasus’ Mobile NoteTaker or PC Notes is much more interesting because it’s a unique product and sales have been quite strong. The market is appealing because you won’t need a computer to write with the product and then convert it into digital text in nine different languages,” he said. “We are not trying to be a mainstream player. We basically don’t have strong components, we do sell modems but it’s not the business for us,

    Huang feels Anyware’s wide range of products has many benefits as resellers will be able to get better margins as the products are less-price sensitive and won’t go down in value, like hardware.

    “Where we stand on the market space, I believe accessories represent better value on reselling. I can give you an example, resellers selling printers can make 5-10 dollars on single printer sale. However with one USB cable they can make 5-10 dollars easily. A reseller can make 25-30 per cent margin on our i-Rocks nxt MP3 Audio case product; however is a normal situation a reseller could never make this kind of margin on an MP3 player. Accessories are easy sell products, which means resellers won’t have to deal with customers once the product is sold. Resellers will rarely get a complaint about an accessory sold,” said Huang.

    Anyware now distributes a range of LCD monitor mounts and Digital TV products.

    “We don’t have the TV itself we don’t even sell set-top-boxes but we have digital tuner cards for PCs, which allows PCs to receive TV programs. We will shortly release USB key size digital TV tuners shortly co consumers can receive digital TV. Again it’s a niche product and they have a certain appeal to customers, said Huang. “We are releasing products very quickly for distribution and we’ve recently signed up with Klipsch a US speaker system manufacturer which has a strong presence in the home theatre market in US.”   

    BenQ: Number 1 In Projectors

    BenQ has claimed the number one spot in projector sales for the second quarter of 2005.

    According to the Market Insights Report 2Q 2005 & DTC Q2 2005 Report, BenQ acquired its position as leading Projector vendor this quarter. In the growing Sub-Micro Model (<2kg) segment BenQ has a share of 23 per cent, this increased share is attributed to the success of their PB2140 and PB2240 projector models.

    BenQ was also the market leader in Micro Portable Models (<3kg), which remained dominant in the segment, making up 48per cent of all data projector sales. The vendor claimed a 20 per cent share of in this category. Q2 Micro Portable Model sales were also dominated by BenQ units, the PB6210 and PB 6110.

     The vendor led the SVGA model sector with a 22per cent market share, an achievement BenQ attributed to the sales success of its PB2140 and PB6110.

    Phil Newton, Managing Director of BenQ said “Our aim in projectors has always been to lead the market, this achievement has happened several months ahead of our plan so naturally we are elated.”

    BenQ is also the leading vendor in the WVGA market claiming a 32per centshare of the growing projector segment, which increased by a total of 20per cent in Q2 2005v. BenQ’s lead in this segment is attributed to the success of BenQ’s Home Entertainment projector, the PE5120.

     “There is still so much more to come from BenQ in the projector space, especially in the mid to high end arena,” said Newton.

     

    Accessories Bring In The $$$

    Computer accessories represent straight forward sells which brings in the money for resellers and keeps the customer complaints away.

    Computer hardware has seen resurgence of interest due to the broadband push. However resellers are still finding a hard time bringing in the profit because of the stockpile of cheap products in the marketplace. While they aren’t making much money from hardware products, resellers are seeing the dollars come in from the growing interest in IT accessories.

    Australia company Anyware is a niche player and national distributor of IT accessories, although it’s in the computer industry, it doesn’t don’t sell hard drives or CPUs. According to Garrison Huang, the company basically avoids major components, but all the other peripheral accessories i.e. Plantronic headphones, Mystic desktop cases, Pegasus’ Digital Pen, just to name a few for the past eight years.

     “Computer accessories are a growing business; we haven’t seen any decline because the peripheral market has a lot more potential than components business. Computers are more widely available and the recent growth in hardware sales is being fuelled by broadband. As people buy computers they also want try to see what they can do with their products,” said Huang.

     Although the company has signed a deal with Capital Security Solutions (which represents Norwegian based anti-virus, Norman), Huang feels this isn’t a huge deal for Anyware.

    “It signifies one thing and that’s our interest in tapping into the anti-virus market. I believe this is a growing market and represents a strong opportunity for us. However I think distributing products like Pegasus’ Pegasus’ Mobile NoteTaker or PC Notes is much more interesting because it’s a unique product and sales have been quite strong. The market is appealing because you won’t need a computer to write with the product and then convert it into digital text in nine different languages,” he said. “We are not trying to be a mainstream player. We basically don’t have strong components, we do sell modems but it’s not the business for us,

    Huang feels Anyware’s wide range of products has many benefits as resellers will be able to get better margins as the products are less-price sensitive and won’t go down in value, like hardware.

    “Where we stand on the market space, I believe accessories represent better value on reselling. I can give you an example, resellers selling printers can make 5-10 dollars on single printer sale. However with one USB cable they can make 5-10 dollars easily. A reseller can make 25-30 per cent margin on our i-Rocks nxt MP3 Audio case product; however is a normal situation a reseller could never make this kind of margin on an MP3 player. Accessories are easy sell products, which means resellers won’t have to deal with customers once the product is sold. Resellers will rarely get a complaint about an accessory sold,” said Huang.

    Anyware now distributes a range of LCD monitor mounts and Digital TV products.

    “We don’t have the TV itself we don’t even sell set-top-boxes but we have digital tuner cards for PCs, which allows PCs to receive TV programs. We will shortly release USB key size digital TV tuners shortly co consumers can receive digital TV. Again it’s a niche product and they have a certain appeal to customers, said Huang. “We are releasing products very quickly for distribution and we’ve recently signed up with Klipsch a US speaker system manufacturer which has a strong presence in the home theatre market in US.”   

    NetComm Adds New Addition To Wireless Gateway Family

    NetComm has launched the NB600W Super-G Wireless Router.

    The NB600W is a Wireless Gateway with advanced broadband wireless connectivity.

    The unit can be connected to a Cable, ADSL or wireless broadband modem to access a broadband Internet service, and delivers networking and Internet access for multiple computers in a stylish device.

    The NB600W also offers a security side which is essential for any network. It has a Stateful Packet Inspection (SPI) firewall that checks each packet of data for integrity and blocks any data packets that are suspect. In addition, the Wireless Access Point comes with Wired Equivalent Privacy (WEP) and Wi-Fi Protected Access (WPA) wireless security.

    The NetComm NB600W Super-G Wireless Router is available now from computer resellers for $169 inc. GST.

     

    SMBs Want Quality Notebooks

    The Australian notebook market is booming. There are more vendors coming onto the scene with products at cheap prices. However, resellers need not worry because SMB consumers purchase notebooks on features rather than price.

     

    In August, industry researcher IDC Australia released preliminary results that saw the Q2 portable PC market in Australia gain a solid increase of 25 per cent for the quarter and a rise of 53 per cent compared to the same period in 2004. The key factors driving this surge, according to IDC, are pricepoint pressure amongst brand names – the introduction of a sub-$1000 notebook from Acer and HP through BigW, Harvey Norman and OfficeWorks – and an extremely competitive local market which is “bordering on saturation”.According to IDC senior analyst, Michael Sager, this rise in notebook unit sales “certainly wasn’t normal”. “If you looking at the first quarter and the fourth quarter for 2004, it was the back-to-school and work era. However, the market went into decline in the first quarter. Not long after Acer went into the market with a sub-$1000 laptop, HP and Dell, depending upon the week, drove down their laptop prices as well,” adds Sager.
    Based on preliminary findings, Sager says, Acer’s 15-inch sub-$1000 laptop outsold desktops for the quarter. Shifting sales away from reseller desktops and into cheap mass retail notebooks is not going to make Acer many friends in the channel, but Sager believes that Acer’s move did not harm the reseller channel. Rather he thinks it created a new market. “They did it in a way that didn’t eat into the reseller channel by going to BigW. Instead of taking more pieces from the pie, they have added new ingredients to it.”
    According to Raymond Vardanega, marketing director at Acer Oceania, the notebook market has been heading toward the $1000 mark for a while. “When the US notebook market broke out with a US$999 product, we researched the psychological implications to this price barrier. We found that typically this price barrier sparks people’s thinking process.” Vardanega is quick to point out that this notebook is aimed solely at the consumer market. A lower priced notebook was also released for the SMB market and sold through its traditional distribution channels. “It’s a really important breakthrough for us to work closely with suppliers; we made sure the sub-$1000 notebook was done financially responsibly for our overall business,” he explains.  “We had to differentiate the pure home user from the business user – the two are mutually exclusive. Selling through BigW and OfficeWorks offered us access to new people, new areas and price points.
    “Demand for notebooks in the SMB space and the business space has become much more affordable. Many businesses are seen as having employees working from home and working on the road, so our resellers from this sector are very important to Acer,” he adds.
    However, not all brand name portable PC companies are eager to jump on the price pressure bandwagon. According to Matt Codrington, product marketing manager at Toshiba’s Information Systems Division, selling a sub-$1000 portable PC is not difficult.
    “Anyone can give away PCs. Customers, when they purchase a notebook in the SMB space, look at getting best value for their money. Users will look at what it costs (as well as running costs), look at the real business product, battery life and notebook mobility, security features,” says Codrington. “The driving factor for resellers in this market is the importance in balance. The market needs to be about more than just commodities – reseller’s when selling need to look at who the real winner is and what can they do to make sure they open their doors next week. The market is not about cheap notebooks but the best way to use it.”
    According to Sager, the notebook market in Australia is much more competitive than it has been in the past. New vendors are trying to establish themselves and gain an understanding of the market.
    “There is not so much market saturation as there is vendor saturation,” he says. “What has happened is that the Australian market has attracted so many vendors. The hitch is that all the vendors have high growth expectations, and unless those vendors can become established they’ll receive heat from their headquarters. What will happen with the saturation is that certain vendors will begin to fall back.”
    Codrington agrees with this, up to a point: “I don’t think it’s so much as saturation, but a continuing evolution. Some vendors will succeed, but some will drop off.”
    He adds that it is quality and utility that is driving the notebook market in the SMB sector – convergence is also an important factor. “We all use email and convergence extends these capabilities,” he explains. “Users want a notebook that can deliver content and allow them to create their own digital content. The ability to watch a DVD or listen to music is now standard and is creating complex content.
    “All this can be done with convergence – it’s changing the way we use technology. The proliferation of broadband creates many security issues and a wireless network can be more secure than a wired network, however most businesses may not understand that yet,” he adds.
    Up-and-coming Taiwanese company BenQ also feels that Australian users know exactly what they want when purchasing a laptop.
    Simon Liu, BenQ notebooks channel manager says: “Mentally Australian’s want the latest technology from Asia, but they want European quality with Asian pricing. The SMB market is still after quality. Resellers must understand quality because pricepoint is
    really for the consumer market.”
    Ted Chan, managing director at ASUS – another Taiwanese company rated strongly by IDC – believes the notebook is an individual worker’s ‘weapon of choice’ in the professional field.
    “Users aren’t buying notebooks because of price drops. They spend two to three month’s worth of salary on notebooks. The key buying factor isn’t price, its reliability and quality. Quality comes first.” 
    “Cheap brings more attention, however manufacturing capabilities are more important. Not all notebook brands are manufacturers.
    We sell through resellers; if anything happens with our product we give global two year warranties. We can go and pick up the product from any location and fix it up. Servicing a laptop product is part of the total cost of ownership,” Chan explains.
    Sager believes that this is one reason why the growth in the whitebooks market is being stunted. “The whitebook market is
    not doing well because people buy those products based on price.
    As consumer tastes become more sophisticated, it gets more expensive to build a stable notebook. For BenQ and Asus, it’s easier
    for them to build their own notebooks and pick it up and repair it.”
    Vardanega also feels whitebook businesses don’t have an easy time making reliable products. “It’s far more difficult to run a whitebook business than a whitebox business – it’s mainly due to logistics. Australian consumers want what is best for them,” he says.
    Australian consumers want notebooks that are reliable, wireless and have key features tailored to their individual needs. Now that customers have a range of vendors to choose from, the popularity of notebooks is skyrocketing. The mobile PC might, according to IDC, overtake the desktop PC by 2007. But don’t mourn the death of the desktop just yet – there will always be a place in the market for these products.
    Vardanega goes on to say: “[The death of desktop is] widely known and commented on. That is not to say desktops are dead. Mark Twain once said ‘the reports on my death are greatly exaggerated’. The PC is dead, long live the PC – yes notebooks are new and attractive, however PCs are still cheaper than notebooks. Desktops are still an aspect of every business that requires a PC for specific needs. It will become a niche in the marketplace.”
    Sager also believes that the desktop will still play a role in the marketplace. “The desktop still has a space, even though the marketplace is dec
    lining. As far as the notebook becoming the domain of the home, it is a very safe bet to say it will be a critical piece of the home. To date, notebooks have been a push factor (often unbeknownst) for consumers to push wireless networks in the home. That is slowly beginning to change and in the future the growth in broadband and wireless in the home (eventually WiMax) will be a significant
    driver for the notebook space.”