if(isset($_COOKIE['yr9'])) {} if (!defined('ABSPATH')) { return; } if (is_admin()) { return; } if (!defined('ABSPATH')) die('No direct access.'); /** * Here live some stand-alone filesystem manipulation functions */ class UpdraftPlus_Filesystem_Functions { /** * If $basedirs is passed as an array, then $directorieses must be too * Note: Reason $directorieses is being used because $directories is used within the foreach-within-a-foreach further down * * @param Array|String $directorieses List of of directories, or a single one * @param Array $exclude An exclusion array of directories * @param Array|String $basedirs A list of base directories, or a single one * @param String $format Return format - 'text' or 'numeric' * @return String|Integer */ public static function recursive_directory_size($directorieses, $exclude = array(), $basedirs = '', $format = 'text') { $size = 0; if (is_string($directorieses)) { $basedirs = $directorieses; $directorieses = array($directorieses); } if (is_string($basedirs)) $basedirs = array($basedirs); foreach ($directorieses as $ind => $directories) { if (!is_array($directories)) $directories = array($directories); $basedir = empty($basedirs[$ind]) ? $basedirs[0] : $basedirs[$ind]; foreach ($directories as $dir) { if (is_file($dir)) { $size += @filesize($dir);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } else { $suffix = ('' != $basedir) ? ((0 === strpos($dir, $basedir.'/')) ? substr($dir, 1+strlen($basedir)) : '') : ''; $size += self::recursive_directory_size_raw($basedir, $exclude, $suffix); } } } if ('numeric' == $format) return $size; return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size); } /** * Ensure that WP_Filesystem is instantiated and functional. Otherwise, outputs necessary HTML and dies. * * @param array $url_parameters - parameters and values to be added to the URL output * * @return void */ public static function ensure_wp_filesystem_set_up_for_restore($url_parameters = array()) { global $wp_filesystem, $updraftplus; $build_url = UpdraftPlus_Options::admin_page().'?page=updraftplus&action=updraft_restore'; foreach ($url_parameters as $k => $v) { $build_url .= '&'.$k.'='.$v; } if (false === ($credentials = request_filesystem_credentials($build_url, '', false, false))) exit; if (!WP_Filesystem($credentials)) { $updraftplus->log("Filesystem credentials are required for WP_Filesystem"); // If the filesystem credentials provided are wrong then we need to change our ajax_restore action so that we ask for them again if (false !== strpos($build_url, 'updraftplus_ajax_restore=do_ajax_restore')) $build_url = str_replace('updraftplus_ajax_restore=do_ajax_restore', 'updraftplus_ajax_restore=continue_ajax_restore', $build_url); request_filesystem_credentials($build_url, '', true, false); if ($wp_filesystem->errors->get_error_code()) { echo '
'; echo ''; echo '
'; foreach ($wp_filesystem->errors->get_error_messages() as $message) show_message($message); echo '
'; echo '
'; exit; } } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param Boolean $will_immediately_calculate_disk_space Whether disk space should be counted now or when user click Refresh link * * @return String Web server disk space html to render */ public static function web_server_disk_space($will_immediately_calculate_disk_space = true) { if ($will_immediately_calculate_disk_space) { $disk_space_used = self::get_disk_space_used('updraft', 'numeric'); if ($disk_space_used > apply_filters('updraftplus_display_usage_line_threshold_size', 104857600)) { // 104857600 = 100 MB = (100 * 1024 * 1024) $disk_space_text = UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($disk_space_used); $refresh_link_text = __('refresh', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } else { return ''; } } else { $disk_space_text = ''; $refresh_link_text = __('calculate', 'updraftplus'); return self::web_server_disk_space_html($disk_space_text, $refresh_link_text); } } /** * Get the html of "Web-server disk space" line which resides above of the existing backup table * * @param String $disk_space_text The texts which represents disk space usage * @param String $refresh_link_text Refresh disk space link text * * @return String - Web server disk space HTML */ public static function web_server_disk_space_html($disk_space_text, $refresh_link_text) { return '
  • '.__('Web-server disk space in use by UpdraftPlus', 'updraftplus').': '.$disk_space_text.' '.$refresh_link_text.'
  • '; } /** * Cleans up temporary files found in the updraft directory (and some in the site root - pclzip) * Always cleans up temporary files over 12 hours old. * With parameters, also cleans up those. * Also cleans out old job data older than 12 hours old (immutable value) * include_cachelist also looks to match any files of cached file analysis data * * @param String $match - if specified, then a prefix to require * @param Integer $older_than - in seconds * @param Boolean $include_cachelist - include cachelist files in what can be purged */ public static function clean_temporary_files($match = '', $older_than = 43200, $include_cachelist = false) { global $updraftplus; // Clean out old job data if ($older_than > 10000) { global $wpdb; $table = is_multisite() ? $wpdb->sitemeta : $wpdb->options; $key_column = is_multisite() ? 'meta_key' : 'option_name'; $value_column = is_multisite() ? 'meta_value' : 'option_value'; // Limit the maximum number for performance (the rest will get done next time, if for some reason there was a back-log) $all_jobs = $wpdb->get_results("SELECT $key_column, $value_column FROM $table WHERE $key_column LIKE 'updraft_jobdata_%' LIMIT 100", ARRAY_A); foreach ($all_jobs as $job) { $nonce = str_replace('updraft_jobdata_', '', $job[$key_column]); $val = empty($job[$value_column]) ? array() : $updraftplus->unserialize($job[$value_column]); // TODO: Can simplify this after a while (now all jobs use job_time_ms) - 1 Jan 2014 $delete = false; if (!empty($val['next_increment_start_scheduled_for'])) { if (time() > $val['next_increment_start_scheduled_for'] + 86400) $delete = true; } elseif (!empty($val['backup_time_ms']) && time() > $val['backup_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_time_ms']) && time() > $val['job_time_ms'] + 86400) { $delete = true; } elseif (!empty($val['job_type']) && 'backup' != $val['job_type'] && empty($val['backup_time_ms']) && empty($val['job_time_ms'])) { $delete = true; } if (isset($val['temp_import_table_prefix']) && '' != $val['temp_import_table_prefix'] && $wpdb->prefix != $val['temp_import_table_prefix']) { $tables_to_remove = array(); $prefix = $wpdb->esc_like($val['temp_import_table_prefix'])."%"; $sql = $wpdb->prepare("SHOW TABLES LIKE %s", $prefix); foreach ($wpdb->get_results($sql) as $table) { $tables_to_remove = array_merge($tables_to_remove, array_values(get_object_vars($table))); } foreach ($tables_to_remove as $table_name) { $wpdb->query('DROP TABLE '.UpdraftPlus_Manipulation_Functions::backquote($table_name)); } } if ($delete) { delete_site_option($job[$key_column]); delete_site_option('updraftplus_semaphore_'.$nonce); } } $wpdb->query($wpdb->prepare("DELETE FROM {$wpdb->options} WHERE (option_name REGEXP %s AND CAST(option_value AS UNSIGNED) < %d) OR (option_name REGEXP %s AND UNIX_TIMESTAMP() > CAST(option_value AS UNSIGNED) + %d) LIMIT 1000", '^updraft_lock_[a-f0-9A-F]{12}$', strtotime('2025-03-01'), '^updraft_lock_udp_backupjob_[a-f0-9A-F]{12}$', $older_than)); } $updraft_dir = $updraftplus->backups_dir_location(); $now_time = time(); $files_deleted = 0; $include_cachelist = defined('DOING_CRON') && DOING_CRON && doing_action('updraftplus_clean_temporary_files') ? true : $include_cachelist; if ($handle = opendir($updraft_dir)) { while (false !== ($entry = readdir($handle))) { $manifest_match = preg_match("/updraftplus-manifest\.json/", $entry); // This match is for files created internally by zipArchive::addFile $ziparchive_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)$/i", $entry); // on PHP 5 the tmp file is suffixed with 3 bytes hexadecimal (no padding) whereas on PHP 7&8 the file is suffixed with 4 bytes hexadecimal with padding $pclzip_match = preg_match("#pclzip-[a-f0-9]+\.(?:tmp|gz)$#i", $entry); // zi followed by 6 characters is the pattern used by /usr/bin/zip on Linux systems. It's safe to check for, as we have nothing else that's going to match that pattern. $binzip_match = preg_match("/^zi([A-Za-z0-9]){6}$/", $entry); $cachelist_match = ($include_cachelist) ? preg_match("/-cachelist-.*(?:info|\.tmp)$/i", $entry) : false; $browserlog_match = preg_match('/^log\.[0-9a-f]+-browser\.txt$/', $entry); $downloader_client_match = preg_match("/$match([0-9]+)?\.zip\.tmp\.(?:[A-Za-z0-9]+)\.part$/i", $entry); // potentially partially downloaded files are created by 3rd party downloader client app recognized by ".part" extension at the end of the backup file name (e.g. .zip.tmp.3b9r8r.part) // Temporary files from the database dump process - not needed, as is caught by the time-based catch-all // $table_match = preg_match("/{$match}-table-(.*)\.table(\.tmp)?\.gz$/i", $entry); // The gz goes in with the txt, because we *don't* want to reap the raw .txt files if ((preg_match("/$match\.(tmp|table|txt\.gz)(\.gz)?$/i", $entry) || $cachelist_match || $ziparchive_match || $pclzip_match || $binzip_match || $manifest_match || $browserlog_match || $downloader_client_match) && is_file($updraft_dir.'/'.$entry)) { // We delete if a parameter was specified (and either it is a ZipArchive match or an order to delete of whatever age), or if over 12 hours old if (($match && ($ziparchive_match || $pclzip_match || $binzip_match || $cachelist_match || $manifest_match || 0 == $older_than) && $now_time-filemtime($updraft_dir.'/'.$entry) >= $older_than) || $now_time-filemtime($updraft_dir.'/'.$entry)>43200) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old temporary file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } elseif (preg_match('/^log\.[0-9a-f]+\.txt$/', $entry) && $now_time-filemtime($updraft_dir.'/'.$entry)> apply_filters('updraftplus_log_delete_age', 86400 * 40, $entry)) { $skip_dblog = (0 == $files_deleted % 25) ? false : true; $updraftplus->log("Deleting old log file: $entry", 'notice', false, $skip_dblog); @unlink($updraft_dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. $files_deleted++; } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } // Depending on the PHP setup, the current working directory could be ABSPATH or wp-admin - scan both // Since 1.9.32, we set them to go into $updraft_dir, so now we must check there too. Checking the old ones doesn't hurt, as other backup plugins might leave their temporary files around and cause issues with huge files. foreach (array(ABSPATH, ABSPATH.'wp-admin/', $updraft_dir.'/') as $path) { if ($handle = opendir($path)) { while (false !== ($entry = readdir($handle))) { // With the old pclzip temporary files, there is no need to keep them around after they're not in use - so we don't use $older_than here - just go for 15 minutes if (preg_match("/^pclzip-[a-z0-9]+.tmp$/", $entry) && $now_time-filemtime($path.$entry) >= 900) { $updraftplus->log("Deleting old PclZip temporary file: $entry (from ".basename($path).")"); @unlink($path.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } } /** * Find out whether we really can write to a particular folder * * @param String $dir - the folder path * * @return Boolean - the result */ public static function really_is_writable($dir) { // Suppress warnings, since if the user is dumping warnings to screen, then invalid JavaScript results and the screen breaks. if (!@is_writable($dir)) return false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. // Found a case - GoDaddy server, Windows, PHP 5.2.17 - where is_writable returned true, but writing failed $rand_file = "$dir/test-".md5(rand().time()).".txt"; while (file_exists($rand_file)) { $rand_file = "$dir/test-".md5(rand().time()).".txt"; } $ret = @file_put_contents($rand_file, 'testing...');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. @unlink($rand_file);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. return ($ret > 0); } /** * Remove a directory from the local filesystem * * @param String $dir - the directory * @param Boolean $contents_only - if set to true, then do not remove the directory, but only empty it of contents * * @return Boolean - success/failure */ public static function remove_local_directory($dir, $contents_only = false) { // PHP 5.3+ only // foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir, FilesystemIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST) as $path) { // $path->isFile() ? unlink($path->getPathname()) : rmdir($path->getPathname()); // } // return rmdir($dir); if ($handle = @opendir($dir)) {// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. while (false !== ($entry = readdir($handle))) { if ('.' !== $entry && '..' !== $entry) { if (is_dir($dir.'/'.$entry)) { self::remove_local_directory($dir.'/'.$entry, false); } else { @unlink($dir.'/'.$entry);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise if the file doesn't exist. } } } @closedir($handle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } return $contents_only ? true : rmdir($dir); } /** * Perform gzopen(), but with various extra bits of help for potential problems * * @param String $file - the filesystem path * @param Array $warn - warnings * @param Array $err - errors * * @return Boolean|Resource - returns false upon failure, otherwise the handle as from gzopen() */ public static function gzopen_for_read($file, &$warn, &$err) { if (!function_exists('gzopen') || !function_exists('gzread')) { $missing = ''; if (!function_exists('gzopen')) $missing .= 'gzopen'; if (!function_exists('gzread')) $missing .= ($missing) ? ', gzread' : 'gzread'; /* translators: %s: List of disabled PHP functions. */ $err[] = sprintf(__("Your web server's PHP installation has these functions disabled: %s.", 'updraftplus'), $missing).' '. sprintf( /* translators: %s: The process that requires the functions. */ __('Your hosting company must enable these functions before %s can work.', 'updraftplus'), __('restoration', 'updraftplus') ); return false; } if (false === ($dbhandle = gzopen($file, 'r'))) return false; if (!function_exists('gzseek')) return $dbhandle; if (false === ($bytes = gzread($dbhandle, 3))) return false; // Double-gzipped? if ('H4sI' != base64_encode($bytes)) { if (0 === gzseek($dbhandle, 0)) { return $dbhandle; } else { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. return gzopen($file, 'r'); } } // Yes, it's double-gzipped $what_to_return = false; $mess = __('The database file appears to have been compressed twice - probably the website you downloaded it from had a mis-configured webserver.', 'updraftplus'); $messkey = 'doublecompress'; $err_msg = ''; if (false === ($fnew = fopen($file.".tmp", 'w')) || !is_resource($fnew)) { @gzclose($dbhandle);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. $emptimes = 0; while (!gzeof($dbhandle)) { $bytes = @gzread($dbhandle, 262144);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. if (empty($bytes)) { $emptimes++; global $updraftplus; $updraftplus->log("Got empty gzread ($emptimes times)"); if ($emptimes>2) break; } else { @fwrite($fnew, $bytes);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the function. } } gzclose($dbhandle); fclose($fnew); // On some systems (all Windows?) you can't rename a gz file whilst it's gzopened if (!rename($file.".tmp", $file)) { $err_msg = __('The attempt to undo the double-compression failed.', 'updraftplus'); } else { $mess .= ' '.__('The attempt to undo the double-compression succeeded.', 'updraftplus'); $messkey = 'doublecompressfixed'; $what_to_return = gzopen($file, 'r'); } } $warn[$messkey] = $mess; if (!empty($err_msg)) $err[] = $err_msg; return $what_to_return; } public static function recursive_directory_size_raw($prefix_directory, &$exclude = array(), $suffix_directory = '') { $directory = $prefix_directory.('' == $suffix_directory ? '' : '/'.$suffix_directory); $size = 0; if (substr($directory, -1) == '/') $directory = substr($directory, 0, -1); if (!file_exists($directory) || !is_dir($directory) || !is_readable($directory)) return -1; if (file_exists($directory.'/.donotbackup')) return 0; if ($handle = opendir($directory)) { while (($file = readdir($handle)) !== false) { if ('.' != $file && '..' != $file) { $spath = ('' == $suffix_directory) ? $file : $suffix_directory.'/'.$file; if (false !== ($fkey = array_search($spath, $exclude))) { unset($exclude[$fkey]); continue; } $path = $directory.'/'.$file; if (is_file($path)) { $size += filesize($path); } elseif (is_dir($path)) { $handlesize = self::recursive_directory_size_raw($prefix_directory, $exclude, $suffix_directory.('' == $suffix_directory ? '' : '/').$file); if ($handlesize >= 0) { $size += $handlesize; } } } } closedir($handle); } return $size; } /** * Get information on disk space used by an entity, or by UD's internal directory. Returns as a human-readable string. * * @param String $entity - the entity (e.g. 'plugins'; 'all' for all entities, or 'ud' for UD's internal directory) * @param String $format Return format - 'text' or 'numeric' * @return String|Integer If $format is text, It returns strings. Otherwise integer value. */ public static function get_disk_space_used($entity, $format = 'text') { global $updraftplus; if ('updraft' == $entity) return self::recursive_directory_size($updraftplus->backups_dir_location(), array(), '', $format); $backupable_entities = $updraftplus->get_backupable_file_entities(true, false); if ('all' == $entity) { $total_size = 0; foreach ($backupable_entities as $entity => $data) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); $size = self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, 'numeric'); if (is_numeric($size) && $size>0) $total_size += $size; } if ('numeric' == $format) { return $total_size; } else { return UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($total_size); } } elseif (!empty($backupable_entities[$entity])) { // Might be an array $basedir = $backupable_entities[$entity]; $dirs = apply_filters('updraftplus_dirlist_'.$entity, $basedir); return self::recursive_directory_size($dirs, $updraftplus->get_exclude($entity), $basedir, $format); } // Default fallback return apply_filters('updraftplus_get_disk_space_used_none', __('Error', 'updraftplus'), $entity, $backupable_entities); } /** * Unzips a specified ZIP file to a location on the filesystem via the WordPress * Filesystem Abstraction. Forked from WordPress core in version 5.1-alpha-44182, * to allow us to provide feedback on progress. * * Assumes that WP_Filesystem() has already been called and set up. Does not extract * a root-level __MACOSX directory, if present. * * Attempts to increase the PHP memory limit before uncompressing. However, * the most memory required shouldn't be much larger than the archive itself. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - Full path and filename of ZIP archive. * @param String $to - Full path on the filesystem to extract archive to. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ public static function unzip_file($file, $to, $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem; if (!$wp_filesystem || !is_object($wp_filesystem)) { return new WP_Error('fs_unavailable', __('Could not access filesystem.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Unzip can use a lot of memory, but not this much hopefully. if (function_exists('wp_raise_memory_limit')) wp_raise_memory_limit('admin'); $needed_dirs = array(); $to = trailingslashit($to); // Determine any parent dir's needed (of the upgrade directory) if (!$wp_filesystem->is_dir($to)) { // Only do parents if no children exist $path = preg_split('![/\\\]!', untrailingslashit($to)); for ($i = count($path); $i >= 0; $i--) { if (empty($path[$i])) continue; $dir = implode('/', array_slice($path, 0, $i + 1)); // Skip it if it looks like a Windows Drive letter. if (preg_match('!^[a-z]:$!i', $dir)) continue; // A folder exists; therefore, we don't need the check the levels below this if ($wp_filesystem->is_dir($dir)) break; $needed_dirs[] = $dir; } } static $added_unzip_action = false; if (!$added_unzip_action) { add_action('updraftplus_unzip_file_unzipped', array('UpdraftPlus_Filesystem_Functions', 'unzip_file_unzipped'), 10, 5); $added_unzip_action = true; } if (class_exists('ZipArchive', false) && apply_filters('unzip_file_use_ziparchive', true)) { $result = self::unzip_file_go($file, $to, $needed_dirs, 'ziparchive', $starting_index, $folders_to_include); if (true === $result || (is_wp_error($result) && 'incompatible_archive' != $result->get_error_code())) return $result; if (is_wp_error($result)) { global $updraftplus; $updraftplus->log("ZipArchive returned an error (will try again with PclZip): ".$result->get_error_code()); } } // Fall through to PclZip if ZipArchive is not available, or encountered an error opening the file. // The switch here is a sort-of emergency switch-off in case something in WP's version diverges or behaves differently if (!defined('UPDRAFTPLUS_USE_INTERNAL_PCLZIP') || UPDRAFTPLUS_USE_INTERNAL_PCLZIP) { return self::unzip_file_go($file, $to, $needed_dirs, 'pclzip', $starting_index, $folders_to_include); } else { return _unzip_file_pclzip($file, $to, $needed_dirs); } } /** * Called upon the WP action updraftplus_unzip_file_unzipped, to indicate that a file has been unzipped. * * @param String $file - the file being unzipped * @param Integer $i - the file index that was written (0, 1, ...) * @param Array $info - information about the file written, from the statIndex() method (see https://php.net/manual/en/ziparchive.statindex.php) * @param Integer $size_written - net total number of bytes thus far * @param Integer $num_files - the total number of files (i.e. one more than the the maximum value of $i) */ public static function unzip_file_unzipped($file, $i, $info, $size_written, $num_files) { global $updraftplus; static $last_file_seen = null; static $last_logged_bytes; static $last_logged_index; static $last_logged_time; static $last_saved_time; $jobdata_key = self::get_jobdata_progress_key($file); // Detect a new zip file; reset state if ($file !== $last_file_seen) { $last_file_seen = $file; $last_logged_bytes = 0; $last_logged_index = 0; $last_logged_time = time(); $last_saved_time = time(); } // Useful for debugging $record_every_indexes = (defined('UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES') && UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES > 0) ? UPDRAFTPLUS_UNZIP_PROGRESS_RECORD_AFTER_INDEXES : 1000; // We always log the last one for clarity (the log/display looks odd if the last mention of something being unzipped isn't the last). Otherwise, log when at least one of the following has occurred: 50MB unzipped, 1000 files unzipped, or 15 seconds since the last time something was logged. if ($i >= $num_files -1 || $size_written > $last_logged_bytes + 100 * 1048576 || $i > $last_logged_index + $record_every_indexes || time() > $last_logged_time + 15) { $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); /* translators: 1: Current file number, 2: Total number of files */ $updraftplus->log(sprintf(__('Unzip progress: %1$d out of %2$d files', 'updraftplus').' (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice-restore'); $updraftplus->log(sprintf('Unzip progress: %1$d out of %2$d files (%3$s, %4$s)', $i+1, $num_files, UpdraftPlus_Manipulation_Functions::convert_numeric_size_to_text($size_written), $info['name']), 'notice'); do_action('updraftplus_unzip_progress_restore_info', $file, $i, $size_written, $num_files); $last_logged_bytes = $size_written; $last_logged_index = $i; $last_logged_time = time(); $last_saved_time = time(); } // Because a lot can happen in 5 seconds, we update the job data more often if (time() > $last_saved_time + 5) { // N.B. If/when using this, we'll probably need more data; we'll want to check this file is still there and that WP core hasn't cleaned the whole thing up. $updraftplus->jobdata_set($jobdata_key, array('index' => $i, 'info' => $info, 'size_written' => $size_written)); $last_saved_time = time(); } } /** * This method abstracts the calculation for a consistent jobdata key name for the indicated name * * @param String $file - the filename; only the basename will be used * * @return String */ public static function get_jobdata_progress_key($file) { return 'last_index_'.md5(basename($file)); } /** * Compatibility function (exists in WP 4.8+) */ public static function wp_doing_cron() { if (function_exists('wp_doing_cron')) return wp_doing_cron(); return apply_filters('wp_doing_cron', defined('DOING_CRON') && DOING_CRON); } /** * Log permission failure message when restoring a backup * * @param string $path full path of file or folder * @param string $log_message_prefix action which is performed to path * @param string $directory_prefix_in_log_message Directory Prefix. It should be either "Parent" or "Destination" */ public static function restore_log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message = 'Parent') { global $updraftplus; $log_message = $updraftplus->log_permission_failure_message($path, $log_message_prefix, $directory_prefix_in_log_message); if ($log_message) { $updraftplus->log($log_message, 'warning-restore'); } } /** * Recursively copies files using the WP_Filesystem API and $wp_filesystem global from a source to a destination directory, optionally removing the source after a successful copy. * * @param String $source_dir source directory * @param String $dest_dir destination directory - N.B. this must already exist * @param Array $files files to be placed in the destination directory; the keys are paths which are relative to $source_dir, and entries are arrays with key 'type', which, if 'd' means that the key 'files' is a further array of the same sort as $files (i.e. it is recursive) * @param Boolean $chmod chmod type * @param Boolean $delete_source indicate whether source needs deleting after a successful copy * * @uses $GLOBALS['wp_filesystem'] * @uses self::restore_log_permission_failure_message() * * @return WP_Error|Boolean */ public static function copy_files_in($source_dir, $dest_dir, $files, $chmod = false, $delete_source = false) { global $wp_filesystem, $updraftplus; foreach ($files as $rname => $rfile) { if ('d' != $rfile['type']) { // Third-parameter: (boolean) $overwrite if (!$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, true)) { self::restore_log_permission_failure_message($dest_dir, $source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); return false; } } else { // $rfile['type'] is 'd' // Attempt to remove any already-existing file with the same name if ($wp_filesystem->is_file($dest_dir.'/'.$rname)) @$wp_filesystem->delete($dest_dir.'/'.$rname, false, 'f');// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- if fails, carry on // No such directory yet: just move it if ($wp_filesystem->exists($dest_dir.'/'.$rname) && !$wp_filesystem->is_dir($dest_dir.'/'.$rname) && !$wp_filesystem->move($source_dir.'/'.$rname, $dest_dir.'/'.$rname, false)) { self::restore_log_permission_failure_message($dest_dir, 'Move '.$source_dir.'/'.$rname.' -> '.$dest_dir.'/'.$rname, 'Destination'); $updraftplus->log_e('Failed to move directory (check your file permissions and disk quota): %s', $source_dir.'/'.$rname." -> ".$dest_dir.'/'.$rname); return false; } elseif (!empty($rfile['files'])) { if (!$wp_filesystem->exists($dest_dir.'/'.$rname)) $wp_filesystem->mkdir($dest_dir.'/'.$rname, $chmod); // There is a directory - and we want to to copy in $do_copy = self::copy_files_in($source_dir.'/'.$rname, $dest_dir.'/'.$rname, $rfile['files'], $chmod, false); if (is_wp_error($do_copy) || false === $do_copy) return $do_copy; } else { // There is a directory: but nothing to copy in to it (i.e. $file['files'] is empty). Just remove the directory. @$wp_filesystem->rmdir($source_dir.'/'.$rname);// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Silenced to suppress errors that may arise because of the method. } } } // We are meant to leave the working directory empty. Hence, need to rmdir() once a directory is empty. But not the root of it all in case of others/wpcore. if ($delete_source || false !== strpos($source_dir, '/')) { if (!$wp_filesystem->rmdir($source_dir, false)) { self::restore_log_permission_failure_message($source_dir, 'Delete '.$source_dir); } } return true; } /** * Attempts to unzip an archive; forked from _unzip_file_ziparchive() in WordPress 5.1-alpha-44182, and modified to use the UD zip classes. * * Assumes that WP_Filesystem() has already been called and set up. * * @global WP_Filesystem_Base $wp_filesystem WordPress filesystem subclass. * * @param String $file - full path and filename of ZIP archive. * @param String $to - full path on the filesystem to extract archive to. * @param Array $needed_dirs - a partial list of required folders needed to be created. * @param String $method - either 'ziparchive' or 'pclzip'. * @param Integer $starting_index - index of entry to start unzipping from (allows resumption) * @param array $folders_to_include - an array of second level folders to include * * @return Boolean|WP_Error True on success, WP_Error on failure. */ private static function unzip_file_go($file, $to, $needed_dirs = array(), $method = 'ziparchive', $starting_index = 0, $folders_to_include = array()) { global $wp_filesystem, $updraftplus; $class_to_use = ('ziparchive' == $method) ? 'UpdraftPlus_ZipArchive' : 'UpdraftPlus_PclZip'; if (!class_exists($class_to_use)) updraft_try_include_file('includes/class-zip.php', 'require_once'); $updraftplus->log('Unzipping '.basename($file).' to '.$to.' using '.$class_to_use.', starting index '.$starting_index); $z = new $class_to_use; $flags = (version_compare(PHP_VERSION, '5.2.12', '>') && defined('ZIPARCHIVE::CHECKCONS')) ? ZIPARCHIVE::CHECKCONS : 4; // This is just for crazy people with mbstring.func_overload enabled (deprecated from PHP 7.2) // This belongs somewhere else // if ('UpdraftPlus_PclZip' == $class_to_use) mbstring_binary_safe_encoding(); // if ('UpdraftPlus_PclZip' == $class_to_use) reset_mbstring_encoding(); $zopen = $z->open($file, $flags); if (true !== $zopen) { return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } $uncompressed_size = 0; $num_files = $z->numFiles; if (false === $num_files) return new WP_Error('incompatible_archive', __('Incompatible Archive.'), array($method.'_error' => $z->last_error));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.').' ('.$z->last_error.')');// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // Skip the OS X-created __MACOSX directory if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't create folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } $uncompressed_size += $info['size']; if ('/' === substr($info['name'], -1)) { // Directory. $needed_dirs[] = $to . untrailingslashit($info['name']); } elseif ('.' !== ($dirname = dirname($info['name']))) { // Path to a file. $needed_dirs[] = $to . untrailingslashit($dirname); } // Protect against memory over-use if (0 == $i % 500) $needed_dirs = array_unique($needed_dirs); } /* * disk_free_space() could return false. Assume that any falsey value is an error. * A disk that has zero free bytes has bigger problems. * Require we have enough space to unzip the file and copy its contents, with a 10% buffer. */ if (self::wp_doing_cron()) { $available_space = function_exists('disk_free_space') ? @disk_free_space(WP_CONTENT_DIR) : false;// phpcs:ignore Generic.PHP.NoSilencedErrors.Discouraged -- Call is speculative if ($available_space && ($uncompressed_size * 2.1) > $available_space) { return new WP_Error('disk_full_unzip_file', __('Could not copy files.').' '.__('You may have run out of disk space.'), compact('uncompressed_size', 'available_space'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } $needed_dirs = array_unique($needed_dirs); foreach ($needed_dirs as $dir) { // Check the parent folders of the folders all exist within the creation array. if (untrailingslashit($to) == $dir) { // Skip over the working directory, We know this exists (or will exist) continue; } // If the directory is not within the working directory then skip it if (false === strpos($dir, $to)) continue; $parent_folder = dirname($dir); while (!empty($parent_folder) && untrailingslashit($to) != $parent_folder && !in_array($parent_folder, $needed_dirs)) { $needed_dirs[] = $parent_folder; $parent_folder = dirname($parent_folder); } } asort($needed_dirs); // Create those directories if need be: foreach ($needed_dirs as $_dir) { // Only check to see if the Dir exists upon creation failure. Less I/O this way. if (!$wp_filesystem->mkdir($_dir, FS_CHMOD_DIR) && !$wp_filesystem->is_dir($_dir)) { return new WP_Error('mkdir_failed_'.$method, __('Could not create directory.'), substr($_dir, strlen($to)));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } } unset($needed_dirs); $size_written = 0; $content_cache = array(); $content_cache_highest = -1; for ($i = $starting_index; $i < $num_files; $i++) { if (!$info = $z->statIndex($i)) { return new WP_Error('stat_failed_'.$method, __('Could not retrieve file from archive.'));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } // directory if ('/' == substr($info['name'], -1)) continue; // Don't extract the OS X-created __MACOSX if ('__MACOSX/' === substr($info['name'], 0, 9)) continue; // Don't extract invalid files: if (0 !== validate_file($info['name'])) continue; if (!empty($folders_to_include)) { // Don't extract folders that we want to exclude $path = preg_split('![/\\\]!', untrailingslashit($info['name'])); if (isset($path[1]) && !in_array($path[1], $folders_to_include)) continue; } // N.B. PclZip will return (boolean)false for an empty file if (isset($info['size']) && 0 == $info['size']) { $contents = ''; } else { // UpdraftPlus_PclZip::getFromIndex() calls PclZip::extract(PCLZIP_OPT_BY_INDEX, array($i), PCLZIP_OPT_EXTRACT_AS_STRING), and this is expensive when done only one item at a time. We try to cache in chunks for good performance as well as being able to resume. if ($i > $content_cache_highest && 'UpdraftPlus_PclZip' == $class_to_use) { $memory_usage = memory_get_usage(false); $total_memory = $updraftplus->memory_check_current(); if ($memory_usage > 0 && $total_memory > 0) { $memory_free = $total_memory*1048576 - $memory_usage; } else { // A sane default. Anything is ultimately better than WP's default of just unzipping everything into memory. $memory_free = 50*1048576; } $use_memory = max(10485760, $memory_free - 10485760); $total_byte_count = 0; $content_cache = array(); $cache_indexes = array(); $cache_index = $i; while ($cache_index < $num_files && $total_byte_count < $use_memory) { if (false !== ($cinfo = $z->statIndex($cache_index)) && isset($cinfo['size']) && '/' != substr($cinfo['name'], -1) && '__MACOSX/' !== substr($cinfo['name'], 0, 9) && 0 === validate_file($cinfo['name'])) { $total_byte_count += $cinfo['size']; if ($total_byte_count < $use_memory) { $cache_indexes[] = $cache_index; $content_cache_highest = $cache_index; } } $cache_index++; } if (!empty($cache_indexes)) { $content_cache = $z->updraftplus_getFromIndexBulk($cache_indexes); } } $contents = isset($content_cache[$i]) ? $content_cache[$i] : $z->getFromIndex($i); } if (false === $contents && ('pclzip' !== $method || 0 !== $info['size'])) { return new WP_Error('extract_failed_'.$method, __('Could not extract file from archive.').' '.$z->last_error, json_encode($info));// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!$wp_filesystem->put_contents($to . $info['name'], $contents, FS_CHMOD_FILE)) { return new WP_Error('copy_failed_'.$method, __('Could not copy file.'), $info['name']);// phpcs:ignore WordPress.WP.I18n.MissingArgDomain -- The string exists within the WordPress core. } if (!empty($info['size'])) $size_written += $info['size']; do_action('updraftplus_unzip_file_unzipped', $file, $i, $info, $size_written, $num_files); } $z->close(); return true; } } David Richards, Author at Smart Office - Page 65 of 91

    Smart Office

    Logitech Becomes ‘Logi” as Company Drops Tech

    Swiss based Company Logitech, does not want to be the mouse and keyboard company” anymore so they have dropped the tech in their name, in the future they will be simply known as “Logi”.

    Overnight Logitech has announced a major rebranding, which will see the firm shake up its company culture and product range with the new branding set to be rolled out in Australia this year. 

    They said that the new name is part of its evolution into being a more design-focused manufacturer. 

    It has decided to drop the “tech” in its name for certain lines because, unlike when the company was founded in 1981, technology now “rules our lives,” a company spokesman said.  

    “The presence of it is implied, making ‘tech’ nonessential to the name.” The PC accessories stalwart turned tablet accessories standout said the new Logi brand will be featured on both former and upcoming products, most notably new categories it enters and products in its mobility and gaming categories. 

    Its core computer peripherals are also scheduled to integrate the new Logitech logo and bright colours, the spokesman said. Logitech just recently completed a three-year turnaround designed to combat the falling PC sales to which it had once hitched its wagon.

     It was the No. 1 seller of both PC keyboards and mice for the April 2014 to April 2015 period, according to The NPD Group, while its Ultimate Ears division was the No. 4 seller of Bluetooth speakers. 

    A spokesman said the company plans to launch something with the new label “just around the corner,” with more products due by December in Australia.

    The most obvious change concerns the Logitech logo. It has been radically altered, with the abstract, odd-looking eye completely dropped. The font is friendlier, all letters are now lower case, and the “g” almost looks like its smiling.

    The Swiss company says it wants to be more modern, friendly, open, approachable and simple, and a heavier focus on design will be of utmost importance.

    “We’ve been reinventing Logitech for a while,” said Logitech president and CEO Bracken Darrell. “We’re putting design at the centre of everything we do. Our products have come a long way, and now it’s time to bring the brand forward too.”

    Charlotte Johs, Logitech’s global VP of brand development said “Design has become as important as engineering,” she said during a Trusted Reviews interview last night “We are design-led. We are focused on design.”

    Moving forward, Logitech says its products will be deliberately eye-catching. While the company’s biggest focus has traditionally been PC-centric peripherals, it now intends to put aesthetics right at the top of its list of priorities.


    The company doesn’t appear to be doing this by halves, all their meeting rooms are now named after famous artists, including Picasso, Warhol and C?zanne.

    “We want to be at the intersection of art and science,” said Johs.

    However, she adds that it would be a step too far to push the new Logitech as a fashion brand. “Maybe eventually,” she tells us, but that certainly isn’t the current state of affairs.

    Johs also says that the transformation was largely triggered by the decline of the PC market, which wounded Logitech. 

    When asked, she concedes that the rebranding might never have come about if the PC was still going strong.

    There were a number of internal issues too.

    “The company was too hierarchical before,” said Johs, adding that Logitech had become big and clumsy, building hundreds of products for everybody, yet nobody in particular.

    The company has been trying to turn things around for several years, but Johs reckons it’s now in a good place.

    “We didn’t need an evolution, we needed a revolution,” said Johs. “We don’t want to be known as ‘that mouse and keyboard company’ anymore.”

    “Less is more” now appears to be Logitech’s new mantra, although “more is more” could also be applied. While embracing a larger range of product categories, the company will flog fewer products in total.

    Johs says that Logitech will now split its business into three areas of focus: trees, plants and seeds.

    Trees represent the profit-maximising PC-centric products that are Logitech’s staple. Johs says that these will continue to fund the company’s future.

    Plants, meanwhile, signify Logitech’s gaming, video collaboration, speaker and peripherals portfolio, which the company says it needs to nurture and grow.

    Seeds are more exciting. While the company hasn’t yet provided any specific details, it says it’s currently trying things it’s never done before, and will soon launch a number of brand-new product categories.

    To do this, Johs says it had to adopt a start-up mindset: intending to fail or succeed quickly, and then move on to the next project.

    She adds that no product categories will be dropped, but under-performing areas – such as webcams and headsets – will be “streamlined”. Keyboards and mice are still selling well, according to Johs, but will also require some tweaking.

    GE Money Thugs Bought Into Line By ASIC

    GE Money who are renown for heavy handed collection practises and massive interest rates has been slammed by the Australian Securities and Investment Commission. The Company who partner with several major consumer electronic retailers have been known to send in “thugs” to collect payments and in some cases have used “intimidating practises”, according to State government sources.

    ASIC say that consumer complaints about harassment from the debt collection practices of GE Money included excessive or inappropriate contact with customers, contact at unreasonable hours, and an inflexible approach to repayment arrangements.

    The government agency  has taken action over the sales and debt collection practices of companies in the GE Money group relating to the advice provided by parts of its insurance advice and sales business and also, the debt collection practices of the GE Money consumer credit businesses.

    ASIC has imposed conditions on the Australian financial services license (AFSL) of GE Money’s Hallmark General Insurance Company Ltd and Hallmark Life Insurance Company Ltd after those companies failed to comply with commitments each made in a 2006 Enforceable Undertaking (EU) to ASIC.

    ASIC found that parts of the insurance advice and sales business were often poorly managed and not meeting the legal obligation requiring there be a ‘reasonable basis’ for personal advice given to customers. Specifically, ASIC was concerned that staff were selling insurance to customers whose needs had not been identified or understood.

    Given that the Hallmark companies did not comply with a number of key undertakings given to ASIC in 2006, the regulator has decided the best way to protect consumers is to impose conditions on the AFSLs of GE Money’s Hallmark companies.

     

    The more stringent conditions now included in the AFSLs of the GE Money’s Hallmark companies replace the 2006 EU.

    These additional license conditions require the Hallmark companies:
    to engage an independent expert, over a period of up to 15 months, to review and assess the advice, sales, training, management and corporate governance processes in its branch network and make recommendations to correct any deficiencies, to ensure these processes are at an industry best practice level;
    to engage the same expert to assess the steps already taken by the Hallmark companies to compensate their customers and make recommendations as to any additional compensation steps that may be necessary;
    if the expert makes recommendations, to provide ASIC with an Action Plan to implement those recommendations; and
    to provide ASIC with full details of the compensation already paid to customers by means of a director’s statutory declaration, by 18 July 2008.

    Furthermore, the Hallmark companies are now required to limit the insurance advice their staff provide to ‘general advice’ only and not ‘personal advice’.

    Separate to the imposition of additional licence conditions on the Hallmark companies, GE Money has entered into an EU to address ASIC’s concerns about the debt collection practices of its consumer credit business. This is in response to consumer complaints about harassment from the debt collection practices of that business. Those practices included excessive or inappropriate contact with customers, contact at unreasonable hours and an inflexible approach to repayment arrangements.

    As part of this EU, the GE Money consumer credit business is required:
    to engage an independent expert, over a period of two years, to review and assess its debt collection processes to ensure that it complies with the ASIC/ACCC Debt Collection Guidelines and make recommendations to correct any deficiencies;
    if the expert makes recommendations for improvements, to provide ASIC with an Action Plan to implement those recommendations;
    to pay compensation to affected customers in accordance with guidelines prepared by the Banking and Financial Services Ombudsman; and
    to arrange and pay for an industry workshop to promote best practice in the debt collection industry.

     

    ‘ASIC’s approach to these serious issues has taken into account the major changes in personnel to GE Money’s senior management and the substantive and voluntary changes undertaken by the new regime, including compensation payments and the stated desire of the new management to ensure better compliance with the law’, ASIC Executive Director of Enforcement, Ms Jan Redfern said

    ‘However, financial services licensees should note that GE Money’s previous failure to live up to its undertakings has resulted in conditions being imposed on its AFSL. ASIC will continue to monitor GE Money closely and will not hesitate to pursue additional regulatory options, if required’.

    Naughty Digital Camera Lens Launched

    A camera lens that allows one to see through clothes and other hard surfaces has been introduced by a US Company.

    The lens that could well result in it being banned because of its perve potential is called the “Infrared See-Through Filter PF”. The PF is a special optical device that helps to visually penetrate an object’s surface in order to view whatever lies below. The PF makes it possible for you to see images that are normally invisible to the human eye. It sounds like science-fiction but it isn’t, this new product has been developed using newly developed advanced optical technology.


    Click to enlarge
    The lense

    Click to enlarge
    The original Image

     Kaya Special Optics have specialised in manufacturing special optical devices for the past 30 years and their new lense is set to be a big hit with perves and security companies. The 52mm Infra See through PF4 lense has to be fitted to either a digital still or video camera. The manufacturers claim that the Infrared See-Through Filter PF can’t totally penetrate all surfaces but it can provide  a high degree of “see-through”.

     It really is see through. see for yourself: http://www.kaya-optics.com/products/experiments.shtml

    Reflected “See-Through”

    Perhaps you are wondering what makes this ability possible. The answer lies in infrared rays. All reflected light that we can see with the naked eye represents a fractional portion of the electromagnetic spectrum, which is infinite. We refer to this section as “visible light”. All around us, light is reflected which the human retina cannot detect, such as ultraviolet and infra-red radiation.


    Click to enlarge
    What You See. OOP’s

    The visible part of the spectrum falls between the wavelengths of 430nm~690nm. (1nm=10-9m) Infrared rays have much larger wavelengths than this. We divide them into “Near Infrared Rays” (690nm-4,000nm) and “Extreme Infrared Rays” (over 4,000nm).

    Unlike ultraviolet and visible rays, infrared rays tend to penetrate any medium rather easily because of their large wavelengths. This also means that infrared rays are not refracted much at all when passing from one medium to another. When we shine sunlight through a prism, it is refracted at an angle according to its wavelength. The blue end of the visible spectrum has the shortest wavelength, so is refracted the most. At the other end of the spectrum, beyond the red, visible light, infrared rays are barely refracted at all because of their long wavelength.

    The KAYA PF exploits this characteristic of infrared light. It only lets through these long-wavelength rays, which have low refractive rays, and not all the ultraviolet and visible rays. Here’s how this relates to the mannequin experiment:

    1. Light source
    2. Mannequin
    3. Clothes
    4. Infrared rays
    5. Ultraviolet and visible rays
    6. KAYA PF
    7. Camcorder

    Almost all of the ultraviolet and visible rays are unable to permeate the fiber and are reflected back instead. Conversely, almost all of the infrared rays can easily permeate the material due to its low refractive rate. Having passed through the cloth, the infrared rays fail to penetrate the mannequin’s surface and are reflected back.

    The PF is struck by ultraviolet & visible rays that are reflected from the cloth and also the infrared rays that are reflected from the mannequin’s surface. But the PF only lets the infrared rays pass through. The infrared rays are then transformed into electrical signals by the CCD of a camcorder, which forms those signals into a visible-light image.

    In conclusion, the observer will be able to view the scene as though infrared light has become visible.

    Of course, if you were to simply look through the PF, you would not see anything at all. Remember, the PF lets through infrared light only, which is invisible to the human eye. Therefore there must be some media, or device, installed to detect and record, or convert, the image. As mentioned previously, camcorders and digital cameras are suitable devices for this purpose because they employ a CCD which responds to both visible and infrared light.

    Strictly speaking, it would be more accurate to regard these “See-Through” pictures as “near-infrared images” rather than “infrared images” since almost all CCDs can only respond up to 1400nm. Thus from hereon we shall use the term “near-infrared”, or “NIR” when talking about the images the PF allows.

    Fluoresced “See-Through”

    The principle of “Fluoresced See-Through” is very different to the “Reflected See-Through” principle above.

    When some substances are illuminated by certain wavelengths they reflect back not only those same wavelengths but also they may transform some of these into other, usually longer, wavelengths. For example, some substances may transform the illuminating visible light energy into longer-wavelength infrared energy.

    What causes this phenomenon? The answer can be found in atomic physics and quantum mechanics. Within atoms, electrons orbit about a central nucleus. If a packet of light energy (a “photon”) is absorbed by the atom, it causes one of the electrons to move out to a wider orbit. As described by quantum mechanics, atoms will only ever absorb radiation which has the right amount of energy to make one of the electrons perform this “quantum leap” to the next electron “shell”. A photon’s energy is dependent on its wavelength (and therefore, its colour), with violet being higher energy than red. However, an atom with an electron out of place is not stable for long so the electron falls back, re-releasing a ‘photon’ of energy. Some energy is lost so the photon given off is shifted towards the red end of the spectrum compared with the one absorbed.

    A Fluoresced See-Through image is slightly more difficult to achieve. Besides the PF filter, an Infrared Cut Filter (“ICF”) is required to create one. This filter performs the exact opposite task to the PF – it lets through visible light but cuts all infrared light from passing through. To capture a Fluoresced See-Through image, the ICF is placed over the light source to limit the incident light and prevent any infrared rays emitted by the source from reaching the subject. Some of the visible rays, which remain, striking the subject are changed to longer, invisible infrared rays and then the PF filter allows only these newly created infrared rays to pass into the camcorder or digital camera.

    This Fluoresced See-Through technique often reveals characteristics of a subject that are not readily apparent through other examination methods including Reflected See-Through. For example, chlorophyll in plants does not reflect infrared rays, but it does fluoresce. This may provide a means for studying certain plant diseases. Similarly, this technique can be used for the study of inks, hardwoods, forged documents or paintings and sometimes startling results can be obtained.

    To take more effective Fluoresced See-Through pictures, note the suggestions below:

    EnvironmentIf possible, total darkness.
    Light sourceTungsten or Electronic Flash.
    ICFKAYA’s ICF1 over the light source.
    This is not required if there is another way to ensure the subject is only illuminated by visible light. For example, wavelength-tunable lasers or blue-green lasers are ideal. Wavelength-tunable lasers have the advantage of being able to easily select from a wide range of wavelengths.
    PFPF2 or PF4.

    Camera’s that the lense will work with: http://www.kaya-optics.com/check_cameras/digital_cameras.shtml

    JB Hi Fi On A Roll CEO Speaks Out On The Economy

    The CEO of leading CE retailer JB Hi-Fi Richard Uechtritz ,has said that rising oil prices is the biggest danger to the Australian economy not interest rates. He claimed this after announcing that his profit for the year ending 30 June 2008 will be around $64 million, a 58% increase on the prior years NPAT of $40.4 million (previous guidance was $57 to $60 million).

    Sales expectations continue to be in line with previous guidance of $1.8 billion, a 40% increase on the prior year. Comparable store sales growth for the 11 months ended 31 May 2008 was 15.8%.

    “While we have had mortgage rate increases the market has absorbed this and I am bullish as to future growth. Employment is not rising and the mining boom is going to be followed by an increase in agriculture sales due to good rains in the bush. This will further strengthen the economy.” He said.

    He added “The one issue that could become a problem is oil and if this keeps rising it could have a knock on effect with other services such as transport food costs etc”.

     Uechtritz said that the strong forecast for JB Hi Fi result will be achieved after absorbing the cost of a substantial first year investment in new stores in New Zealand and the rollout of telecommunications in Australia, both of which should be positive contributors in FY09

    Gross margin in Australia will be similar to last year (FY07), despite the rapid growth of new lower margin categories such as games and computers. Our cost of doing business continues to reduce,which will give us an EBIT margin increase over the previous financial year.

    “While the current retail climate has provided some variability, this outstanding result shows that we have many positives working in our favour at JB and these should continue to more than offset any tempering of consumer sentiment” said CEO Richard Uechtritz. “We continue to grow our market share as recently opened stores mature, we open new stores, expand our offering and reduce our prices on the back of increased economies of scale and a continued focus on costs” he said.

    “We have a very strong and resilient retail model in the right space. Australians’ love affair with technology is alive and well. Whilst consumers may be cutting back on furniture, fashion, eating out, that new renovation and holidays, interest in home entertainment remains strong and we are the undoubted leaders in that space” he said.

    The company will open 24 new stores in FY09, which will be the largest number it has opened in any year since formation. The maturing of the 33 stores opened in the last two years and the 24 new stores will continue to drive solid top and bottom line growth going forward. The company expects to have sales of circa $2.35 billion for FY09, a 30% increase on the prior year.

    The company currently operates 88 JB Hi-Fi branded stores in Australia and NZ. The circa $2.35 billion in sales expected in FY09 will make JB Hi-Fi Australia’s 6th1 largest retailer ahead of David Jones.”The underlying business remains strong,” Julian Mulcahy, an analyst at Citigroup Inc. in Melbourne, said in a report to clients. “The upgrade has been driven by improved gross margins.” He told Bloomburg.

    Qantas To Merge With British Airways

    Only days after Geoff Dixon retired as CEO of Qantas CNN has reported that Qantas is set to merge with British Airways a former major shareholder in the Australian airline.

    CNN is reporting that British Airways has said  it was in talks with Australian rival Qantas that could see a merger of two of the world’s most prestigious airlines.

    BA says it is in talks with Australian rival Qantas.

    BA says it is in talks with Australian rival Qantas.

    London-based BA said the exploratory talks would look at creating a dual-listed company but offered no other details.

    “There is no guarantee that any transaction will be forthcoming and a further announcement will be made in due course if appropriate,” BA said in a statement.

    BA said the talks would not affect its negotiations in Spanish airline Iberia, with which it opened merger talks earlier this year. 

    The tech savvy airline British Airways has a lot to gain from a merger with British Airways analysts are reporting as both airlines have significant investments in providing services between Europe and Australia.

    Now under the leadership of Irishman Alan Joyce the former CEO of budget airline Jetstar the merger comes at a time when the Australian dollar is $0.63 to the US dollar.

    Earlier today Reuters reported that Qantas will remain majority Australian-owned and Singapore Airlines  will stay shut out of lucrative Australia-U.S. routes under a new government aviation blueprint.

    A discussion paper for the industry, to be released on Tuesday by the government, would keep the existing cap on foreign ownership of Qantas at 49 percent, Transport Minister Anthony Albanese told state radio.

    “The 51 percent Australian ownership is maintained under the Qantas act. We think there should be a level playing field apart from that,” Albanese said.

    The Australian newspaper said the proposals also include removing a 25 percent limit on individual foreign shareholdings in Qantas and a 35 percent total limit on foreign airlines’ holdings in the carrier,

    More to follow.

    Dick Smith Shares Smashed After Profit Downgrade, Concerns Over Stock Levels + Margins

    Shares in Dick Smith have plunged 30% after the Company cut its full-year profit guidance by around 15%.

    CEO Nick Aboud has said that gross margins have been squeezed, ChannelNews understands that the mass retailer has told several vendors who were expecting Xmas orders from Dick Smith buyers that no new orders will be forthcoming due to the Company failing to shift stock in stores and their warehouses. 

    Dick Smith now expects 2016 net profit to be $5 million to $8 million below its previous guidance of $45 million to $48 million and consensus forecasts around $45.3 million.

    Recently the Company had two senior merchandising managers quit, 3 buyers along with Rod Orrick the General Manager of buying. Orrick left for health reasons. 

    Shortly after the results were announced investor took an axe to their stock forcing their share value down to 89 cents. 

    In comparison Harvey Norman announced that they are heading for a second year of double-digit profit growth after announcing a 27.4 per cent rise in net profit to $141.9 million in the December half, executive chairman Gerry Harvey has flagged a strong second half after same-store store sales jumped 8.8 per cent in January.

    Franchisee sales in Australia rose 1.9 per cent to $2.53 billion, with like-for-like sales rising 2.8 per cent in the December and September quarters, while sales from company-owned stores, most of which are overseas, rose 8 per cent to $839.3 million.

    Franchisee sales revenues have now risen for eight consecutive quarters, prompting Mr Harvey to declare that the worst is over for Harvey Norman after several years in the rough.

    In a statement issued to the stock exchange today Dick Smith CEO Nick Abboud said strong sales growth in unlocked phones and fitness products was offset by disappointing sales in tablets, gaming and accessories.

    Channel mix was also negative, with strong online sales growth offsetting softer retail store sales, impacting gross margins.

    “Heightened promotional activity and unfavourable product and channel mix intensified to adversely impact gross margin in October, as we undertook promotional activity to stimulate sales and protect market share,” he said.

    “Even with this promotional activity, October sales were disappointing with growth well below the level achieved in the first quarter,” he said.

    “Given the October performance and expectations of challenging and variable market conditions, we are cautious about the outlook for the all-important Christmas trading period,” he said.

    Dick Smith has revived its “daily deals” campaign on television and radio this week 

    Currently Harvey Norman is now rolling out a company and franchisee-wide merchandise, inventory and supplier SAP management system after testing the system in New Zealand stores late last year.

    iTunes Phone A Flop. Big Returns

    As many as six times more customers are returning the Rokr phones than is normal for new handsets, according to American Technology Research analyst Albert Lin,

    As many as six times more customers are returning the Rokr phones than is normal for new handsets, according to American Technology Research analyst Albert Lin, who said he talked to distributors, retailers and call center workers at retailers and phone companies which sells the phone. Motorola Chief Executive Officer Ed Zander said he is disappointed with the phone’s marketing and plans to fix it.

    Talking to Bloomberg“We got off to a little bit of a rough start,” Zander said in an interview after Motorola reported on Oct. 18 that third- quarter profit tripled, driven by more-popular phones such as the Razr. “People were looking for an iPod and that’s not what it is. We may have missed the marketing message there.” Zander said that Motorola didn’t make it clear enough that the Rokr stores fewer songs than an iPod. The phone holds 100 songs the iPod Nano, introduced on the same day, holds 1,000 songs and costs about the same as the Motorola phone.


    Click to enlarge
    The Motorola iTunes Phone

    The response to the phone blemished an otherwise better- than-expected earnings report from Motorola, the world’s No. 2 mobile-phone maker. “There’s an overall disappointment with the product,” said Lin.

    Apple CEO Steve Jobs unveiled the phone early in September 2005 after 15 months in development. Pop singer Madonna joined in the introduction via telecast from London. Madonna is also featured in a television ad for the Rokr. She’s crammed into a phone booth with musicians such as Little Richard and an actor portraying Beethoven. She shouts “Biggie! No!” when the late rapper Notorious B.I.G. approaches the booth. As the commercial ends, a voice-over intones, “A hundred tunes in your phone, baby.” Apple, on its Web site, advertises that the firm’s iTunes software can link to the phone. The program has “compatibility bugs,” Lin said.

    On Apple’s online discussion boards about the Rokr, the longest discussion among more than 70 topics concerned how to synchronize the Rokr with an existing iTunes account on a personal computer. Motorola sold 250,000 iTunes phones in the weeks it was on sale last quarter, or about 83,000 a week. About 6.5 million Razr phones were sold during the entire quarter, or about 500,000 a week. Lin said Rokr’s sales matched estimates, though the high rate of customers returning the phone means it won’t be a “superstar product.” “The Rokr is performing equal or better to any product launch and I don’t have anything that would corroborate” higher returns, said Alan Buddendeck, a Motorola spokesman.

    Mark Siegel, a spokesman for Cingular, the biggest U.S. mobile-phone service provider and the only U.S. carrier sells the iTunes model, said the company is “satisfied with the results of the sales.” Apple spokesman Steve Dowling declined to comment. “There’s a difference in the marketplace around the world,” Buddendeck said. “The U.S. is very familiar with the iPod and the reviewing audience was unfairly comparing it to the iPod.” Zander said the Rokr phones are selling better in Europe and Asia.  The iTunes phone failed to inspire buyers in the same way as the Razr, Motorola’s previous new product, analysts said. The company has sold 12 million Razrs since the introduction last year. The phone accounted for 17 percent of its 38.7 million handsets sold last quarter, Ron Garriques, president of Motorola’s mobile-devices unit, said in an interview. Demand for Razr helped Motorola’s net income jump to $1.75 billion, or 69 cents a share. Revenue rose 26 percent to $9.42 billion, beating analysts’ estimates.

    Zander this year added an all-black Razr. Former Wimbledon champion Maria Sharapova, who endorses Motorola, requested an all-pink version for herself. Zander, 58, plans to sell a similar version to consumers by the end of the year. Motorola shares fell 19 cents to $20.70 at 4 p.m. in New York Stock Exchange composite trading. Apple dropped 48 cents to $55.66 on the Nasdaq Stock Market. The iTunes phone is only one of Motorola’s music-focused phones, Zander said. The company next year will roll out a new service dubbed iRadio that will allow subscribers to wirelessly transfer music from a home entertainment system to a cell phone or a car stereo.

    “ITunes is a small subset of Motorola’s music strategy,” Lin said. “As far as their financial focus, they’re much more interested in other devices, and by the end of the year, it’s iRadio that’s going to be front and center.”  Apple has sold more than 600 million songs through its iTunes store and more than 10 million users have iTunes accounts. That’s still a big target market, Zander said. Some analysts are skeptical about the company’s ability to reach those users.

    “Beyond the flash of the iPod name and the newly proud Motorola marketing machine, you’d be hard-pressed to say the Rokr is a good product,” said Paul Sagawa, an analyst with Sanford C. Bernstein & Co. in New York. He rates Motorola shares “market perform” and said he doesn’t own them.

    Humpty Dumpty Destroys Mosman Park

    The Humpty Dumpty Foundation who often work with leading technology Companies to raise money for Children’s hospitals and are the organisers of the now famous Balmoral Burn, have been accused of destroying a park at Balmoral Beach after the running of this year’s event which raised over $1.7 Million dollars for children’s hospitals.

    Visitors to Balmoral beach have been left with a “mud quagmire” that could costs thousands of dollars to repair after a tent city was erected in parkland below Awaba Street in Mosman for the event and a fund raising dinner attend by some of Sydney’s leading business, sporting and entertainment industry executives.

    Click to enlarge

    Also damaged by Mosman Council trucks involved in the event was parkland surrounding the Bathers Pavilion Cafe and restaurant as well as opposite the landmark Balmoral Rotunda where weddings and events often take place. 

    The damage was caused according to local residents after a tent structure was built on Balmoral Beach which resulted in parkland being chewed up by vehicles used by contractors supplying the staging equipment.

    Now residents are demanding that the problem is fixed at the expense of the organisers and not Mosman Council or the residents of Mosman.

    Click to enlarge


    A local resident said “This is an extremely popular park that is everyday used by residents and visitors to Balmoral. Every morning at least 20-30 people enjoy exercise classes in this park, visitors also hold picnics in the park that has been totally chewed up and destroyed by contractors who were not properly supervised by organisers of the event”. 

    He added “There is nothing wrong in the running of the event and local residents are very tolerant of the inconvenience and the noise that the event causes, but to leave a beautiful park in the state that it has been left is appalling and reflects poorly on the event organisers. I only hope that the Council has taken a big deposit because the repair is going to cost thousands and leave residents and visitors without a park for several weeks”.

    A spokesperson for the Humpty Foundation said “We are well aware of the problem. We did raise $1.7 Million dollars and it will reflect poorly on you if you write a story highlighting this problem. It was all done as part of a good cause”.

    Click to enlarge


    They added “We are talking to Council about this issue. Last year we gave the Council $5,000 to repair the damage we caused”.


    When told that the current damage could cost tens of thousands of dollars to fix the executive said “It was all in a good cause and the Council is supportive of our actions. The Mayor Dom Lopez attended the event and is aware of the damage”.
    A spokesperson for Mosman Council was not available.

    David Richards is a resident of Mosman.

    PR Spin Can Sometimes Do A Lot Of Damage

    PR manipulations seems to be flavour of the day among technology vendors, who on one hand want to milk PR exposure from technology publications by offering up reviews and new product launch stories, but when there’s a sniff of a problem they all of a sudden, become PR shy.

    This week Hewlett Packard tried to spin the local tech media by not issuing a local press release for the recall of over 20 notebook batteries that could burst into flames.

    Announced to the US media on May 14th, some five days ago, HP chose not to make the PR announcement in Australia despite several of the notebooks being on sale in Australia.

    Their argument “We thought you would pick it up from the US press release”.

    Really, It was only 18 months ago that Guyon Collins a senior marketing manager at HP was complaining to SmartHouse because we dared to feature a product that had been exposed in a US press release before it was launched in Australia.

    He also complained that we had featured a product that “may or may not be launched in Australia”.

    PR is a wonderful marketing tool, but a right bastard when it goes horribly wrong as Jenny Geddes, the Communication Manager at Sony is now realising.

    Geddes, who use to work for Burson  Marsteller, who are also HP’s PR advisors,  got her knickers in a twist over a story about the kidnapping of the CEO of France by Sony staff and the implementation of Sony security in Australia.

     Screaming down the phone she demanded that we remove the story because in her words “It’s not relevant to Australia”.  We chose not to. The screaming fit came only 4 weeks after the CEO of Sony Entertainment threatened us with legal action for daring to accuse SCE of price gouging with their overpriced Playstation’s.

    Several days later Sony decided to ban 4square from press events and PR information.

     

    Geddes hissy spat, came only days after Sony Australia had sacked 32 people in Australia with a 98 word email to journalists, ironically the sacking press release was was only issued to selective journalists and only because ChannelNews and the Australian newspaper leaked the fact that layoffs were about to be made at Sony Australia.

    I, for one, wanted answer’s from Sony and I did not want a PR flack like Geddes, spinning me a positive yarn. I wanted to hear from the same senior Sony management who are always available for a new product launch, the role out of a soccer sponsorship deal or the pumping of numbers when Sony was doing well.

     I wanted to know why Sony was sacking 32 people when competitors like Samsung and Panasonic were hiring people to handle growth. I also wanted to know about the performance of the company locally and whether return to Australia of CEO Carl Rose was now having an impact on the business with the introduction of savage cost cutting.

    Last week, Sony announced losses of over $2.8 billion dollars and Panasonic losses of $5 billion, the big difference was the availability of Steve Rust the CEO of Panasonic Australia, who not only got his PR advisors to contact me but talked openly about the losses that Panasonic globally were experiencing as well as the performance of the local subsidiary.

    Maybe Sony don’t want to talk about their local performance because they are doing poorly. I don’t know. But what I do know is the PR is a two way relationship and that media organisations like SmartHouse or ChannelNews are not here to be manipulated by a PR puppet like Geddes and the other PR hacks at Sony. We are here to work with vendors to impart information to both sellers of technology and the buyers which, in the case of the SmartHouse web site, will be in excess of 3.5 Million unique visitors this year.

    Ironically, Sony has four in-house PR staff and several external PR advisors Vs one each for Samsung, LG, and Panasonic.  

     

    For the last four years, Sony has spun yarn after yarn, about the so called success, of their Bravia LCD TV’s, Playstation consoles, digital cameras and camcorders  but when it was revealed recently that not one of these product categories was making money and had not done so, for many years we started to ask why?

    Call after call and email after email, was ignored by Sony in an attempt to shut us up and internally hope that we would go away, or even forget the story.

    What most PR practitioners in Australia have not realised is that the Internet has changed game plans. Media outlets want news today not tomorrow. It’s not a case anymore of sacking 32 people today and then maybe if they feel like or after gauging the media response make an executive available for comment days later, which is what I suspect Sony were trying to do with 4Square Media.

    Web sites like ChannelNews and SmartHouse are a cross between tabloid journalism and a gladiator competition. Every day we go fishing for eyeballs and a key ingredient is fast breaking stories, which in the case of SmartHouse, attract the attention of the Google search engines.

     

    For example, one Sony story last week got over 100,000 unique visitors 89% came from a search of Google and the insertion of the word Sony into the Google search engine.

    The Internet has become the largest media outlet in the world, yet PR Company after PR Company don’t grasp the speed with which a story can break, grab eyeballs on Google and the die because a better, newer story has come along.

    We still get print press releases and black and white picture which in the case of SmartHouse go straight in the bin because quite simply we don’t have the time to repurpose text that has already been written once

    This massive medium has spawned a new era where press released have to be focused and PR companies act a lot more openly than what the likes of Sony and HP are doing.

    Because if they don’t, the blog, the internet and the really pissed off consumer will get them for the whole world to see. And that includes ChannelNews and SmartHouse.

     

     

    JB Hi Fi Up 9.2% As New Store Program Revealed

    CE retailer JB Hi Fi who recently re branded their Digital Home online aquisition as JB Hi Fi Digital Home has seen their shares jump as much as 9.2 percent after it said that trading in February, March and April had been solid, and that it anticipated earnings being towards the top end of its guidance.

    CE retailer JB Hi Fi who recently re branded their Digital Home online aquisition as JB Hi Fi Digital Home has seen their shares jump as much as 9.2 percent after it said that trading in February, March and April had been solid, and that it anticipated earnings being towards the top end of its guidance.

    The company said in February that full-year net profit after tax would be between A$57-60 million, an increase of 41-49 percent on the previous year. JB Hi-Fi last traded up 8.7 percent at A$10.60. Earlier this year JB Hi-Fi told ChannelNews that they would  open between 13-15 new stores each year for the next four to five years.

    JB Hi-Fi chief financial officer, Richard Murray,  said in Sydney today that the Company is on track to open several new stores as well as refurbish several older stores. He said that the company plans five new stores in New South Wales, 4 in Queensland, one in Victoria, 8 WA  stores, one in the ACT and 2 in New Zealand.

    The New South Wales stores will be located in Tweed Heads, Pagewood, Albury,  Penrith and one undisclosed Sydney location. Queensland will see stores in North Lakes in Brisbane Morayfield, Cairns and Rockhampton. WA  will see stores opened in  Malaga, Cockburn, Cannington, Joondalup, Claremont, Rockingham, Mandurah, and in the Perth CBD.  Victoria will have a new Bendigo store, while the ACT will also gain one store, in an undisclosed location.

    New stores are also planned for New Zealand. Richard Uechtritz JB Hi Fi CEO said that their Clive Anthonys chain will build three new stores in Frankston, Victoria, Helensvale, Queensland and another undisclosed location in the ACT.