HEX
Server: Apache/2.4.65 (Debian)
System: Linux 88f31f35b0b8 6.1.0-38-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.1.147-1 (2025-08-02) x86_64
User: www-data (33)
PHP: 8.2.29
Disabled: NONE
Upload Files
File: /var/www/html/wp-content/plugins/tiny-compress-images/src/class-tiny-diagnostics.php
<?php
/*
* Tiny Compress Images - WordPress plugin.
* Copyright (C) 2015-2026 Tinify B.V.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the Free
* Software Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/

/**
 * Collects diagnostic information and generates downloadable zip files.
 *
 * @since 3.7.0
 */
class Tiny_Diagnostics {

	/**
	 * Tiny settings
	 *
	 * @var Tiny_Settings
	 */
	private $settings;

	/**
	 * @param Tiny_Settings $settings
	 */
	public function __construct( $settings ) {
		$this->settings = $settings;

		add_action(
			'wp_ajax_tiny_download_diagnostics',
			array( $this, 'download_diagnostics' )
		);
	}

	/**
	 * Collects all diagnostic information.
	 *
	 * File contains:
	 * - timestamp of export
	 * - server information
	 * - site information
	 * - plugin list
	 * - tinify settings
	 * - image settings
	 * - logs
	 *
	 * @since 3.7.0
	 *
	 * @return array Array of diagnostic information.
	 */
	public function collect_info() {
		$info = array(
			'timestamp'      => current_time( 'Y-m-d H:i:s' ),
			'site_info'      => self::get_site_info(),
			'server_info'    => self::get_server_info(),
			'active_plugins' => self::get_active_plugins(),
			'tiny_info'      => $this->get_tiny_info(),
			'image_sizes'    => $this->settings->get_active_tinify_sizes(),
		);

		return $info;
	}

	/**
	 * Gets server information.
	 * We have considered phpinfo but this would be a security concern
	 * as it contains a lot of information we probably do not need.
	 * Whenever support needs more server information, we can manually
	 * add it here.
	 *
	 * @since 3.7.0
	 *
	 * @return array Server information.
	 */
	private static function get_server_info() {
		global $wpdb;

		return array(
			'php_version'         => phpversion(),
			'server_software'     => isset( $_SERVER['SERVER_SOFTWARE'] ) ?
				sanitize_text_field( wp_unslash( $_SERVER['SERVER_SOFTWARE'] ) ) :
				'Unknown',
			'mysql_version'       => $wpdb->db_version(),
			'max_execution_time'  => ini_get( 'max_execution_time' ),
			'memory_limit'        => ini_get( 'memory_limit' ),
			'post_max_size'       => ini_get( 'post_max_size' ),
			'upload_max_filesize' => ini_get( 'upload_max_filesize' ),
			'max_input_vars'      => ini_get( 'max_input_vars' ),
			'curl_version'        => function_exists( 'curl_version' ) ?
				curl_version()['version'] :
				'Not available',
			'disabled_functions'  => ini_get( 'disable_functions' ),
		);
	}

	/**
	 * Gets site information.
	 *
	 * @since 3.7.0
	 *
	 * @return array Site information.
	 */
	private static function get_site_info() {
		global $wp_version;
		$theme = wp_get_theme();

		return array(
			'wp_version'    => $wp_version,
			'site_url'      => get_site_url(),
			'home_url'      => get_home_url(),
			'is_multisite'  => is_multisite(),
			'site_language' => get_locale(),
			'timezone'      => wp_timezone_string(),
			'theme_name'    => $theme->get( 'Name' ),
			'theme_version' => $theme->get( 'Version' ),
			'theme_uri'     => $theme->get( 'ThemeURI' ),
		);
	}

	/**
	 * Gets list of active plugins.
	 *
	 * @since 3.7.0
	 *
	 * @return array List of active plugins.
	 */
	private static function get_active_plugins() {
		$active_plugins = get_option( 'active_plugins', array() );
		$plugins        = array();

		foreach ( $active_plugins as $plugin ) {
			$plugin_data = get_plugin_data( WP_PLUGIN_DIR . '/' . $plugin );
			$plugins[]   = array(
				'name'    => $plugin_data['Name'],
				'version' => $plugin_data['Version'],
				'author'  => $plugin_data['Author'],
				'file'    => $plugin,
			);
		}

		return $plugins;
	}

	/**
	 * Gets TinyPNG plugin info & settings.
	 *
	 * @since 3.7.0
	 *
	 * @return array Plugin settings
	 */
	private function get_tiny_info() {
		return array(
			'version'              => Tiny_Plugin::version(),
			'status'               => $this->settings->get_status(),
			'php_client_supported' => Tiny_PHP::client_supported(),

			'compression_count'    => $this->settings->get_compression_count(),
			'compression_timing'   => $this->settings->get_compression_timing(),
			'conversion'           => $this->settings->get_conversion_options(),
			'paying_state'         => $this->settings->get_paying_state(),
		);
	}

	public function download_diagnostics() {
		check_ajax_referer( 'tiny-compress', 'security' );

		if ( ! current_user_can( 'manage_options' ) ) {
			wp_send_json_error(
				esc_html__( 'Not allowed to download diagnostics.', 'tiny-compress-images' ),
				403
			);
		}

		$zippath = $this->create_diagnostic_zip();
		return $this->download_zip( $zippath );
	}

	/**
	 * Creates a diagnostic zip file.
	 *
	 * @since 3.7.0
	 *
	 * @return string|WP_Error Path to the created zip file or WP_Error on failure.
	 */
	public function create_diagnostic_zip() {
		if ( ! class_exists( 'ZipArchive' ) ) {
			return new WP_Error(
				'zip_not_available',
				__(
					'ZipArchive class is not available on this server.',
					'tiny-compress-images'
				)
			);
		}

		$wp_filesystem = Tiny_Helpers::get_wp_filesystem();
		$temp_dir      = trailingslashit( get_temp_dir() ) . 'tiny-compress-temp';
		if ( ! $wp_filesystem->exists( $temp_dir ) ) {
			wp_mkdir_p( $temp_dir );
		}

		$temp_path = tempnam( $temp_dir, 'tiny-compress-diagnostics-' . gmdate( 'Y-m-d-His' ) );

		$zip = new ZipArchive();
		if ( true !== $zip->open( $temp_path, ZipArchive::CREATE | ZipArchive::OVERWRITE ) ) {
			return new WP_Error(
				'zip_create_failed',
				__(
					'Failed to create zip file.',
					'tiny-compress-images'
				)
			);
		}

		$info = self::collect_info();
		$zip->addFromString( 'tiny-diagnostics.json', wp_json_encode( $info, JSON_PRETTY_PRINT ) );

		$logger    = Tiny_Logger::get_instance();
		$log_files = $logger->get_log_files();

		foreach ( $log_files as $log_file ) {
			if ( $wp_filesystem->exists( $log_file ) ) {
				$zip->addFile( $log_file, 'logs/' . basename( $log_file ) );
			}
		}

		$zip->close();
		return $temp_path;
	}

	/**
	 * Downloads and removes the zip
	 *
	 * @since 3.7.0
	 *
	 * @param string $zip_path Path to the zip file.
	 */
	public static function download_zip( $zip_path ) {
		$wp_filesystem = Tiny_Helpers::get_wp_filesystem();
		if ( ! $wp_filesystem->exists( $zip_path ) ) {
			wp_die( esc_html__( 'Diagnostic file not found.', 'tiny-compress-images' ) );
		}

		header( 'Content-Type: application/zip' );
		header( 'Content-Disposition: attachment; filename="tiny-compress-diagnostics.zip"' );
		header( 'Content-Length: ' . $wp_filesystem->size( $zip_path ) );
		header( 'Pragma: no-cache' );
		header( 'Expires: 0' );

		// phpcs:ignore WordPress.WP.AlternativeFunctions.file_system_operations_readfile
		readfile( $zip_path );

		// Clean up.
		$wp_filesystem->delete( $zip_path );

		exit;
	}
}