HEX
Server: Apache/2.4.54 (Unix) OpenSSL/1.0.2k-fips
System: Linux f17.eelserver.com 3.10.0-1160.80.1.el7.x86_64 #1 SMP Tue Nov 8 15:48:59 UTC 2022 x86_64
User: zulfiqar (1155)
PHP: 8.2.0
Disabled: mail, exec, system, popen, proc_open, shell_exec, passthru, show_source
Upload Files
File: /home/zulfiqar/public_html/wp-content/plugins/stream/classes/class-log.php
<?php
/**
 * Handles top-level record keeping functionality.
 *
 * @package WP_Stream
 */

namespace WP_Stream;

/**
 * Class - Log
 */
class Log {

	/**
	 * Holds Instance of plugin object
	 *
	 * @var Plugin
	 */
	public $plugin;

	/**
	 * Previous Stream record ID, used for chaining same-session records
	 *
	 * @var int
	 */
	private $prev_record;

	/**
	 * Class constructor.
	 *
	 * @param Plugin $plugin Instance of plugin object.
	 */
	public function __construct( $plugin ) {
		$this->plugin = $plugin;

		// Ensure function used in various methods is pre-loaded.
		if ( ! function_exists( 'is_plugin_active_for_network' ) ) {
			require_once ABSPATH . '/wp-admin/includes/plugin.php';
		}
	}

	/**
	 * Log handler
	 *
	 * @param Connector $connector Connector responsible for logging the event.
	 * @param string    $message sprintf-ready error message string.
	 * @param array     $args sprintf (and extra) arguments to use.
	 * @param int       $object_id Target object id.
	 * @param string    $context Context of the event.
	 * @param string    $action Action of the event.
	 * @param int       $user_id User responsible for the event.
	 *
	 * @return mixed True if updated, otherwise false|WP_Error
	 */
	public function log( $connector, $message, $args, $object_id, $context, $action, $user_id = null ) {
		global $wp_roles;

		if ( is_null( $user_id ) ) {
			$user_id = get_current_user_id();
		}

		if ( is_null( $object_id ) ) {
			$object_id = 0;
		}

		$wp_cron_tracking = isset( $this->plugin->settings->options['advanced_wp_cron_tracking'] ) ? $this->plugin->settings->options['advanced_wp_cron_tracking'] : false;
		$author           = new Author( $user_id );
		$agent            = $author->get_current_agent();

		// WP Cron tracking requires opt-in and WP Cron to be enabled.
		if ( ! $wp_cron_tracking && 'wp_cron' === $agent ) {
			return false;
		}

		$ip_address = $this->plugin->get_client_ip_address();

		$user = new \WP_User( $user_id );

		if ( $this->is_record_excluded( $connector, $context, $action, $user, $ip_address ) ) {
			return false;
		}

		$user_meta = array(
			'user_email'      => (string) ! empty( $user->user_email ) ? $user->user_email : '',
			'display_name'    => (string) $author->get_display_name(),
			'user_login'      => (string) ! empty( $user->user_login ) ? $user->user_login : '',
			'user_role_label' => (string) $author->get_role(),
			'agent'           => (string) $agent,
		);

		if ( 'wp_cli' === $agent && function_exists( 'posix_getuid' ) ) {
			$uid       = posix_getuid();
			$user_info = posix_getpwuid( $uid );

			$user_meta['system_user_id']   = (int) $uid;
			$user_meta['system_user_name'] = (string) $user_info['name'];
		}

		// Prevent any meta with null values from being logged.
		$stream_meta = array_filter(
			$args,
			function ( $value ) {
				return ! is_null( $value );
			}
		);

		// Add user meta to Stream meta.
		$stream_meta['user_meta'] = $user_meta;

		if ( ! empty( $user->roles ) ) {
			$roles = array_values( $user->roles );
			$role  = $roles[0];
		} elseif ( is_multisite() && is_super_admin() && $wp_roles->is_role( 'administrator' ) ) {
			$role = 'administrator';
		} else {
			$role = '';
		}

		$recordarr = array(
			'object_id' => (int) $object_id,
			'site_id'   => (int) is_multisite() ? get_current_site()->id : 1,
			'blog_id'   => (int) apply_filters( 'wp_stream_blog_id_logged', get_current_blog_id() ),
			'user_id'   => (int) $user_id,
			'user_role' => (string) $role,
			'created'   => (string) current_time( 'mysql', true ),
			'summary'   => (string) vsprintf( $message, $args ),
			'connector' => (string) $connector,
			'context'   => (string) $context,
			'action'    => (string) $action,
			'ip'        => (string) $ip_address,
			'meta'      => (array) $stream_meta,
		);

		if ( 0 === $recordarr['object_id'] ) {
			unset( $recordarr['object_id'] );
		}

		$result = $this->plugin->db->insert( $recordarr );

		// This is helpful in development environments:
		// error_log( $this->debug_backtrace( $recordarr ) );.

		return $result;
	}

	/**
	 * This function is use to check whether or not a record should be excluded from the log.
	 *
	 * @param string   $connector Name of the connector being logged.
	 * @param string   $context Name of the context being logged.
	 * @param string   $action Name of the action being logged.
	 * @param \WP_User $user The user being logged.
	 * @param string   $ip IP address being logged.
	 *
	 * @return bool
	 */
	public function is_record_excluded( $connector, $context, $action, $user = null, $ip = null ) {
		$exclude_record = false;

		if ( is_null( $user ) ) {
			$user = wp_get_current_user();
		}

		if ( ! empty( $user->roles ) ) {
			$roles = array_values( $user->roles );
			$role  = $roles[0];
		} else {
			$role = '';
		}
		$record = array(
			'connector'  => $connector,
			'context'    => $context,
			'action'     => $action,
			'author'     => $user->ID,
			'role'       => $role,
			'ip_address' => $ip,
		);

		$exclude_settings = isset( $this->plugin->settings->options['exclude_rules'] ) ? $this->plugin->settings->options['exclude_rules'] : array();

		if ( is_multisite() && $this->plugin->is_network_activated() && ! is_network_admin() ) {
			$multisite_options = (array) get_site_option( 'wp_stream_network', array() );
			$exclude_settings  = isset( $multisite_options['exclude_rules'] ) ? $multisite_options['exclude_rules'] : array();
		}

		foreach ( $this->exclude_rules_by_rows( $exclude_settings ) as $exclude_rule ) {
			$exclude = array(
				'connector'  => ! empty( $exclude_rule['connector'] ) ? $exclude_rule['connector'] : null,
				'context'    => ! empty( $exclude_rule['context'] ) ? $exclude_rule['context'] : null,
				'action'     => ! empty( $exclude_rule['action'] ) ? $exclude_rule['action'] : null,
				'ip_address' => ! empty( $exclude_rule['ip_address'] ) ? $exclude_rule['ip_address'] : null,
				'author'     => is_numeric( $exclude_rule['author_or_role'] ) ? absint( $exclude_rule['author_or_role'] ) : null,
				'role'       => ( ! empty( $exclude_rule['author_or_role'] ) && ! is_numeric( $exclude_rule['author_or_role'] ) ) ? $exclude_rule['author_or_role'] : null,
			);

			$exclude_rules = array_filter(
				$exclude,
				function ( $value ) {
					return ! is_null( $value );
				}
			);

			if ( $this->record_matches_rules( $record, $exclude_rules ) ) {
				$exclude_record = true;
				break;
			}
		}

		/**
		 * Filters whether or not a record should be excluded from the log.
		 *
		 * If true, the record is not logged.
		 *
		 * @param array $exclude_record Whether the record should excluded.
		 * @param array $recordarr The record to log.
		 *
		 * @return bool
		 */
		return apply_filters( 'wp_stream_is_record_excluded', $exclude_record, $record );
	}

	/**
	 * Check if a record to stored matches certain rules.
	 *
	 * @param array $record List of record parameters.
	 * @param array $exclude_rules List of record exclude rules.
	 *
	 * @return boolean
	 */
	public function record_matches_rules( $record, $exclude_rules ) {
		$matches_needed = count( $exclude_rules );
		$matches_found  = 0;
		foreach ( $exclude_rules as $exclude_key => $exclude_value ) {
			if ( ! isset( $record[ $exclude_key ] ) || is_null( $exclude_value ) ) {
				continue;
			}

			if ( 'ip_address' === $exclude_key ) {
				$ip_addresses = explode( ',', $exclude_value );

				if ( in_array( $record['ip_address'], $ip_addresses, true ) ) {
					++$matches_found;
				}
			} elseif ( $record[ $exclude_key ] === $exclude_value ) {
				++$matches_found;
			}
		}

		return $matches_found === $matches_needed;
	}

	/**
	 * Get all exclude rules by row because we store them by rule instead.
	 *
	 * @param array $rules List of rules indexed by rule ID.
	 *
	 * @return array
	 */
	public function exclude_rules_by_rows( $rules ) {
		$excludes = array();

		// TODO: Move these to where the settings are generated to ensure they're in sync.
		$rule_keys = array(
			'exclude_row',
			'author_or_role',
			'connector',
			'context',
			'action',
			'ip_address',
		);

		if ( empty( $rules['exclude_row'] ) ) {
			return array();
		}

		foreach ( array_keys( $rules['exclude_row'] ) as $row_id ) {
			$excludes[ $row_id ] = array();

			foreach ( $rule_keys as $rule_key ) {
				if ( isset( $rules[ $rule_key ][ $row_id ] ) ) {
					$excludes[ $row_id ][ $rule_key ] = $rules[ $rule_key ][ $row_id ];
				} else {
					$excludes[ $row_id ][ $rule_key ] = null;
				}
			}
		}

		return $excludes;
	}

	/**
	 * Helper function to send a full backtrace of calls to the PHP error log for debugging
	 *
	 * @param array $recordarr Record argument array.
	 *
	 * @return string
	 */
	public function debug_backtrace( $recordarr ) {
		if ( version_compare( PHP_VERSION, '5.3.6', '<' ) ) {
			return __( 'Debug backtrace requires at least PHP 5.3.6', 'wp_stream' );
		}

		// Record details.
		$summary   = isset( $recordarr['summary'] ) ? $recordarr['summary'] : null;
		$author    = isset( $recordarr['author'] ) ? $recordarr['author'] : null;
		$connector = isset( $recordarr['connector'] ) ? $recordarr['connector'] : null;
		$context   = isset( $recordarr['context'] ) ? $recordarr['context'] : null;
		$action    = isset( $recordarr['action'] ) ? $recordarr['action'] : null;

		// Stream meta.
		$stream_meta = isset( $recordarr['meta'] ) ? $recordarr['meta'] : null;

		unset( $stream_meta['user_meta'] );

		if ( $stream_meta ) {
			array_walk(
				$stream_meta,
				function ( &$value, $key ) {
					$value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
				}
			);
			$stream_meta = implode( ', ', $stream_meta );
		}

		// User meta.
		$user_meta = isset( $recordarr['meta']['user_meta'] ) ? $recordarr['meta']['user_meta'] : null;

		if ( $user_meta ) {
			array_walk(
				$user_meta,
				function ( &$value, $key ) {
					$value = sprintf( '%s: %s', $key, ( '' === $value ) ? 'null' : $value );
				}
			);

			$user_meta = implode( ', ', $user_meta );
		}

		// Debug backtrace.
		ob_start();

		// @codingStandardsIgnoreStart
		debug_print_backtrace( DEBUG_BACKTRACE_IGNORE_ARGS ); // Option to ignore args requires PHP 5.3.6
		// @codingStandardsIgnoreEnd

		$backtrace = ob_get_clean();
		$backtrace = array_values( array_filter( explode( "\n", $backtrace ) ) );

		$output = sprintf(
			"WP Stream Debug Backtrace\n\n    Summary | %s\n     Author | %s\n  Connector | %s\n    Context | %s\n     Action | %s\nStream Meta | %s\nAuthor Meta | %s\n\n%s\n",
			$summary,
			$author,
			$connector,
			$context,
			$action,
			$stream_meta,
			$user_meta,
			implode( "\n", $backtrace )
		);

		return $output;
	}
}