<?php
namespace Automattic\Gravatar\GravatarEnhanced\Proxy;
require_once __DIR__ . '/class-proxy-cache.php';
require_once __DIR__ . '/class-hash.php';
require_once __DIR__ . '/class-proxy-options.php';
require_once __DIR__ . '/class-proxy-preferences.php';
use Automattic\Gravatar\GravatarEnhanced\Module;
use WP_Error;
class Proxy implements Module {
/**
* @var Options
*/
private $options;
const SCHEDULE_NAME = 'gravatar_enhanced_proxy_flush';
const FILTER_PROXY_FLUSH_FREQUENCY = 'gravatar_proxy_flush_frequency';
const FILTER_PROXY_MAX_DOWNLOAD_TIME = 'gravatar_proxy_max_download_time';
const FILTER_PROXY_MAX_DOWNLOADS = 'gravatar_proxy_max_downloads';
const FILTER_PROXY_SITE_ID = 'gravatar_proxy_site_id';
/**
* Total number of downloads this request
* @var int
*/
private $total_downloads = 0;
/**
* Max number of downloads this request
* @var int
*/
private $max_downloads = 20;
/**
* Max time of downloads this request (seconds)
* @var int
*/
private $max_download_time = 5;
/**
* Time when downloads first started
* @var int
*/
private $time_first_download = 0;
/**
* @param Preferences $preferences
*
*/
public function __construct( $preferences ) {
$this->options = $preferences->get_options();
}
/**
* @return void
*/
public function init() {
register_deactivation_hook( GRAVATAR_ENHANCED_PLUGIN_FILE, [ $this, 'deactivate' ] );
if ( defined( 'IS_PROFILE_PAGE' ) && IS_PROFILE_PAGE ) {
return;
}
add_action( 'admin_init', [ $this, 'admin_init' ] );
if ( $this->options->type !== Options::TYPE_DISABLED ) {
add_filter( 'get_avatar_url', [ $this, 'get_avatar_url' ], 11 );
$this->max_download_time = apply_filters( self::FILTER_PROXY_MAX_DOWNLOAD_TIME, $this->max_download_time );
$this->max_downloads = apply_filters( self::FILTER_PROXY_MAX_DOWNLOADS, $this->max_downloads );
add_action( self::SCHEDULE_NAME, [ $this, 'flush_cache' ] );
}
}
/**
* Main admin_init function used to hook into and register stuff and init plugin settings.
*
* @return void
*/
public function admin_init() {
if ( isset( $_POST['flush_cache'] ) && isset( $_POST['flush_cache_nonce'] ) && wp_verify_nonce( $_POST['flush_cache_nonce'], 'flush_cache' ) ) {
$this->flush_cache();
}
$this->schedule_flush();
}
/**
* Flush the cache
*
* @return void
*/
public function flush_cache() {
$proxy = new ProxyCache();
$proxy->flush();
}
/**
* Plugin deactivation
* @return void
*/
public function deactivate() {
wp_clear_scheduled_hook( self::SCHEDULE_NAME );
}
/**
* Schedule the cache flush
*
* @return void
*/
private function schedule_flush() {
$schedule_time = wp_next_scheduled( self::SCHEDULE_NAME );
$is_enabled = $this->options->type !== Options::TYPE_DISABLED;
// Remove the schedule if proxy is disabled
if ( $schedule_time !== false && ! $is_enabled ) {
$this->deactivate();
}
// Enable the schedule if proxy is enabled
if ( $schedule_time === false && $is_enabled ) {
$frequency = apply_filters( self::FILTER_PROXY_FLUSH_FREQUENCY, 'weekly' );
wp_schedule_event(
time(),
$frequency,
self::SCHEDULE_NAME
);
}
}
/**
* Replace the avatar URL with a SHA256 encoded one
*
* @param string $url
* @return string
*/
public function get_avatar_url( $url ) {
if ( $this->time_first_download === 0 ) {
$this->time_first_download = time();
}
// Can we download anymore avatars?
if ( ! $this->can_download() ) {
return $url;
}
$hash = $this->get_hash_for_url( $url );
$proxy = new ProxyCache();
// Have we already cached this user?
if ( ! $proxy->exists_locally( $hash ) ) {
// No, so try and download it
$result = $proxy->copy_avatar_locally( $hash );
if ( $result instanceof WP_Error ) {
// Failed. Just return the original URL
return $url;
}
$this->total_downloads++;
}
// Return the proxied URL
return $proxy->get_local_url( $hash );
}
/**
* Can we still download?
*
* @return bool
*/
private function can_download() {
// Limit the number of downloads per page load
if ( $this->total_downloads >= $this->max_downloads ) {
return false;
}
// Limit the total time spent downloading. This is a rough calculation and isn't strictly the download time, but it's close enough
if ( time() - $this->time_first_download >= $this->max_download_time ) {
return false;
}
return true;
}
/**
* Get a hash object for the URL based on site settings
*
* @param string $url
* @return AvatarHash
*/
private function get_hash_for_url( $url ) {
if ( $this->options->type === Options::TYPE_PRIVATE ) {
$cache_id = apply_filters( self::FILTER_PROXY_SITE_ID, 'gravatar' );
return new PrivateAvatarHash( $url, (string) $this->options->time, $cache_id );
}
return new LocalAvatarHash( $url );
}
/**
* @return void
*/
public function uninstall() {}
}