364 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
			
		
		
	
	
			364 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			PHP
		
	
	
	
	
	
<?php
 | 
						|
namespace ShortPixel\Controller;
 | 
						|
 | 
						|
if ( ! defined( 'ABSPATH' ) ) {
 | 
						|
 exit; // Exit if accessed directly.
 | 
						|
}
 | 
						|
 | 
						|
use ShortPixel\ShortPixelLogger\ShortPixelLogger as Log;
 | 
						|
 | 
						|
class QuotaController
 | 
						|
{
 | 
						|
    protected static $instance;
 | 
						|
    const CACHE_NAME = 'quotaData';
 | 
						|
 | 
						|
    protected $quotaData;
 | 
						|
 | 
						|
    /** Singleton instance
 | 
						|
    * @return Object QuotaController object
 | 
						|
    */
 | 
						|
    public static function getInstance()
 | 
						|
    {
 | 
						|
      if (is_null(self::$instance))
 | 
						|
        self::$instance = new QuotaController();
 | 
						|
 | 
						|
      return self::$instance;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * Return if account has any quota left
 | 
						|
     * @return boolean Has quota left
 | 
						|
     */
 | 
						|
    public function hasQuota()
 | 
						|
    {
 | 
						|
      $settings = \wpSPIO()->settings();
 | 
						|
 | 
						|
      if ($settings->quotaExceeded)
 | 
						|
			{
 | 
						|
        return false;
 | 
						|
			}
 | 
						|
      return true;
 | 
						|
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Retrieves QuotaData object from cache or from remote source
 | 
						|
     * @return array The quotadata array (remote format)
 | 
						|
     */
 | 
						|
    protected function getQuotaData()
 | 
						|
    {
 | 
						|
        if (! is_null($this->quotaData))
 | 
						|
          return $this->quotaData;
 | 
						|
 | 
						|
        $cache = new CacheController();
 | 
						|
 | 
						|
        $cacheData = $cache->getItem(self::CACHE_NAME);
 | 
						|
 | 
						|
        if (! $cacheData->exists() )
 | 
						|
        {
 | 
						|
            $quotaData = $this->getRemoteQuota();
 | 
						|
						if (! $this->hasQuota())
 | 
						|
							$timeout = MINUTE_IN_SECONDS;
 | 
						|
						else {
 | 
						|
							$timeout = HOUR_IN_SECONDS;
 | 
						|
						}
 | 
						|
            $cache->storeItem(self::CACHE_NAME, $quotaData, $timeout);
 | 
						|
        }
 | 
						|
        else
 | 
						|
				{
 | 
						|
          $quotaData = $cacheData->getValue();
 | 
						|
				}
 | 
						|
 | 
						|
        return $quotaData;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Retrieve quota information for this account
 | 
						|
     * @return object quotadata SPIO format
 | 
						|
     */
 | 
						|
    public function getQuota()
 | 
						|
    {
 | 
						|
 | 
						|
          $quotaData = $this->getQuotaData();
 | 
						|
          $DateNow = time();
 | 
						|
 | 
						|
          $DateSubscription = strtotime($quotaData['APILastRenewalDate']);
 | 
						|
          $DaysToReset =  30 - ( (int) (  ( $DateNow  - $DateSubscription) / 84600) % 30);
 | 
						|
 | 
						|
          $quota = (object) [
 | 
						|
              'unlimited' => isset($quotaData['Unlimited']) ? $quotaData['Unlimited'] : false,
 | 
						|
              'monthly' => (object) [
 | 
						|
                'text' =>  sprintf(__('%s/month', 'shortpixel-image-optimiser'), $quotaData['APICallsQuota']),
 | 
						|
                'total' =>  $quotaData['APICallsQuotaNumeric'],
 | 
						|
                'consumed' => $quotaData['APICallsMadeNumeric'],
 | 
						|
                'remaining' => max($quotaData['APICallsQuotaNumeric'] - $quotaData['APICallsMadeNumeric'], 0),
 | 
						|
                'renew' => $DaysToReset,
 | 
						|
              ],
 | 
						|
              'onetime' => (object) [
 | 
						|
                'text' => $quotaData['APICallsQuotaOneTime'],
 | 
						|
                'total' => $quotaData['APICallsQuotaOneTimeNumeric'],
 | 
						|
                'consumed' => $quotaData['APICallsMadeOneTimeNumeric'],
 | 
						|
                'remaining' => $quotaData['APICallsQuotaOneTimeNumeric'] - $quotaData['APICallsMadeOneTimeNumeric'],
 | 
						|
              ],
 | 
						|
          ];
 | 
						|
 | 
						|
          $quota->total = (object) [
 | 
						|
              'total' => $quota->monthly->total + $quota->onetime->total,
 | 
						|
              'consumed'  => $quota->monthly->consumed + $quota->onetime->consumed,
 | 
						|
              'remaining' =>$quota->monthly->remaining + $quota->onetime->remaining,
 | 
						|
          ];
 | 
						|
 | 
						|
 | 
						|
          return $quota;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Get available remaining - total - credits
 | 
						|
     * @return int Total remaining credits
 | 
						|
     */
 | 
						|
    public function getAvailableQuota()
 | 
						|
    {
 | 
						|
        $quota = $this->getQuota();
 | 
						|
        return $quota->total->remaining;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Force to get quotadata from API, even if cache is still active ( use very sparingly )
 | 
						|
     * Does not actively fetches it, but invalidates cache, so next call will trigger a remote call
 | 
						|
     * @return void
 | 
						|
     */
 | 
						|
    public function forceCheckRemoteQuota()
 | 
						|
    {
 | 
						|
        $cache = new CacheController();
 | 
						|
        $cacheData = $cache->getItem(self::CACHE_NAME);
 | 
						|
        $cacheData->delete();
 | 
						|
        $this->quotaData = null;
 | 
						|
    }
 | 
						|
 | 
						|
    /**
 | 
						|
     * Validate account key in the API via quota check
 | 
						|
     * @param  string $key               User account key
 | 
						|
     * @return array      Quotadata array (remote format) with validated key
 | 
						|
     */
 | 
						|
    public function remoteValidateKey($key)
 | 
						|
    {
 | 
						|
			  // Remove the cache before checking.
 | 
						|
				$this->forceCheckRemoteQuota();
 | 
						|
        return $this->getRemoteQuota($key, true);
 | 
						|
    }
 | 
						|
 | 
						|
		/**
 | 
						|
     * Called when plugin detects the remote quota has been exceeded.
 | 
						|
     * Triggers various call to actions to customer
 | 
						|
     */
 | 
						|
		public function setQuotaExceeded()
 | 
						|
		{
 | 
						|
			  $settings = \wpSPIO()->settings();
 | 
						|
				$settings->quotaExceeded = 1;
 | 
						|
				$this->forceCheckRemoteQuota(); // remove the previous cache.
 | 
						|
		}
 | 
						|
 | 
						|
    /**
 | 
						|
     * When quota is detected again via remote check, reset all call to actions
 | 
						|
     */
 | 
						|
    private function resetQuotaExceeded()
 | 
						|
    {
 | 
						|
        $settings = \wpSPIO()->settings();
 | 
						|
 | 
						|
        AdminNoticesController::resetAPINotices();
 | 
						|
 | 
						|
				// Only reset after a quotaExceeded situation, otherwise it keeps popping.
 | 
						|
				if ($settings->quotaExceeded == 1)
 | 
						|
				{
 | 
						|
						AdminNoticesController::resetQuotaNotices();
 | 
						|
				}
 | 
						|
		//		Log::addDebug('Reset Quota Exceeded and reset Notices');
 | 
						|
       	$settings->quotaExceeded = 0;
 | 
						|
    }
 | 
						|
 | 
						|
 | 
						|
    /**
 | 
						|
     * [getRemoteQuota description]
 | 
						|
     * @param  string $apiKey                 User account key
 | 
						|
     * @param  boolean $validate               Api should also validate key or not
 | 
						|
     * @return array            Quotadata array (remote format) [with validated key]
 | 
						|
     */
 | 
						|
    private function getRemoteQuota($apiKey = false, $validate = false)
 | 
						|
    {
 | 
						|
        if (! $apiKey && ! $validate) // validation is done by apikeymodel, might result in a loop.
 | 
						|
        {
 | 
						|
          $keyControl = ApiKeyController::getInstance();
 | 
						|
          $apiKey = $keyControl->forceGetApiKey();
 | 
						|
        }
 | 
						|
 | 
						|
 | 
						|
        $settings = \wpSPIO()->settings();
 | 
						|
 | 
						|
          if($settings->httpProto != 'https' && $settings->httpProto != 'http') {
 | 
						|
              $settings->httpProto = 'https';
 | 
						|
          }
 | 
						|
 | 
						|
          $requestURL = $settings->httpProto . '://' . SHORTPIXEL_API . '/v2/api-status.php';
 | 
						|
          $args = array(
 | 
						|
              'timeout'=> 15, // wait for 15 secs.
 | 
						|
              'body' => array('key' => $apiKey)
 | 
						|
          );
 | 
						|
          $argsStr = "?key=".$apiKey;
 | 
						|
 | 
						|
					$serverAgent = isset($_SERVER['HTTP_USER_AGENT']) ? urlencode(sanitize_text_field(wp_unslash($_SERVER['HTTP_USER_AGENT']))) : '';
 | 
						|
          $args['body']['useragent'] = "Agent" . $serverAgent;
 | 
						|
          $argsStr .= "&useragent=Agent".$args['body']['useragent'];
 | 
						|
 | 
						|
          // Only used for keyValidation
 | 
						|
          if($validate) {
 | 
						|
 | 
						|
              $statsController = StatsController::getInstance();
 | 
						|
              $imageCount = $statsController->find('media', 'itemsTotal');
 | 
						|
              $thumbsCount = $statsController->find('media', 'thumbsTotal');
 | 
						|
 | 
						|
              $args['body']['DomainCheck'] = get_site_url();
 | 
						|
              $args['body']['Info'] = get_bloginfo('version') . '|' . phpversion();
 | 
						|
              $args['body']['ImagesCount'] = $imageCount; //$imageCount['mainFiles'];
 | 
						|
              $args['body']['ThumbsCount'] = $thumbsCount; // $imageCount['totalFiles'] - $imageCount['mainFiles'];
 | 
						|
              $argsStr .= "&DomainCheck={$args['body']['DomainCheck']}&Info={$args['body']['Info']}&ImagesCount=$imageCount&ThumbsCount=$thumbsCount";
 | 
						|
 | 
						|
 | 
						|
          }
 | 
						|
 | 
						|
          $args['body']['host'] = parse_url(get_site_url(),PHP_URL_HOST);
 | 
						|
          $argsStr .= "&host={$args['body']['host']}";
 | 
						|
					if (defined('SHORTPIXEL_HTTP_AUTH_USER') && defined('SHORTPIXEL_HTTP_AUTH_PASSWORD'))
 | 
						|
					{
 | 
						|
						$args['body']['user'] = stripslashes(SHORTPIXEL_HTTP_AUTH_USER);
 | 
						|
						$args['body']['pass'] = stripslashes(SHORTPIXEL_HTTP_AUTH_PASSWORD);
 | 
						|
						$argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
 | 
						|
					}
 | 
						|
          elseif(! is_null($settings->siteAuthUser) && strlen($settings->siteAuthUser)) {
 | 
						|
 | 
						|
              $args['body']['user'] = stripslashes($settings->siteAuthUser);
 | 
						|
              $args['body']['pass'] = stripslashes($settings->siteAuthPass);
 | 
						|
              $argsStr .= '&user=' . urlencode($args['body']['user']) . '&pass=' . urlencode($args['body']['pass']);
 | 
						|
          }
 | 
						|
          if($settings !== false) {
 | 
						|
              $args['body']['Settings'] = $settings;
 | 
						|
          }
 | 
						|
 | 
						|
          $time = microtime(true);
 | 
						|
          $comm = array();
 | 
						|
 | 
						|
          //Try first HTTPS post. add the sslverify = false if https
 | 
						|
          if($settings->httpProto === 'https') {
 | 
						|
              $args['sslverify'] = apply_filters('shortpixel/system/sslverify', true);
 | 
						|
          }
 | 
						|
 | 
						|
          $response = wp_remote_post($requestURL, $args);
 | 
						|
 | 
						|
          $comm['A: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
 | 
						|
 | 
						|
          //some hosting providers won't allow https:// POST connections so we try http:// as well
 | 
						|
          if(is_wp_error( $response )) {
 | 
						|
 | 
						|
              $requestURL = $settings->httpProto == 'https' ?
 | 
						|
                  str_replace('https://', 'http://', $requestURL) :
 | 
						|
                  str_replace('http://', 'https://', $requestURL);
 | 
						|
              // add or remove the sslverify
 | 
						|
              if($settings->httpProto === 'https') {
 | 
						|
                  $args['sslverify'] = apply_filters('shortpixel/system/sslverify', true);
 | 
						|
              } else {
 | 
						|
                  unset($args['sslverify']);
 | 
						|
              }
 | 
						|
              $response = wp_remote_post($requestURL, $args);
 | 
						|
              $comm['B: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
 | 
						|
 | 
						|
              if(!is_wp_error( $response )){
 | 
						|
                  $settings->httpProto = ($settings->httpProto == 'https' ? 'http' : 'https');
 | 
						|
              } else {
 | 
						|
              }
 | 
						|
          }
 | 
						|
          //Second fallback to HTTP get
 | 
						|
          if(is_wp_error( $response )){
 | 
						|
              $args['body'] = null;
 | 
						|
              $requestURL .= $argsStr;
 | 
						|
              $response = wp_remote_get($requestURL, $args);
 | 
						|
              $comm['C: ' . (number_format(microtime(true) - $time, 2))] = array("sent" => "POST: " . $requestURL, "args" => $args, "received" => $response);
 | 
						|
          }
 | 
						|
          Log::addInfo("API STATUS COMM: " . json_encode($comm));
 | 
						|
 | 
						|
          $defaultData = array(
 | 
						|
              "APIKeyValid" => false,
 | 
						|
              "Message" => __('API Key could not be validated due to a connectivity error.<BR>Your firewall may be blocking us. Please contact your hosting provider and ask them to allow connections from your site to api.shortpixel.com (IP 176.9.21.94).<BR> If you still cannot validate your API Key after this, please <a href="https://shortpixel.com/contact" target="_blank">contact us</a> and we will try to help. ','shortpixel-image-optimiser'),
 | 
						|
              "APICallsMade" => __('Information unavailable. Please check your API key.','shortpixel-image-optimiser'),
 | 
						|
              "APICallsQuota" => __('Information unavailable. Please check your API key.','shortpixel-image-optimiser'),
 | 
						|
              "APICallsMadeOneTime" => 0,
 | 
						|
              "APICallsQuotaOneTime" => 0,
 | 
						|
              "APICallsMadeNumeric" => 0,
 | 
						|
              "APICallsQuotaNumeric" => 0,
 | 
						|
              "APICallsMadeOneTimeNumeric" => 0,
 | 
						|
              "APICallsQuotaOneTimeNumeric" => 0,
 | 
						|
              "APICallsRemaining" => 0,
 | 
						|
              "APILastRenewalDate" => 0,
 | 
						|
              "DomainCheck" => 'NOT Accessible');
 | 
						|
          $defaultData = is_array($settings->currentStats) ? array_merge( $settings->currentStats, $defaultData) : $defaultData;
 | 
						|
 | 
						|
          if(is_object($response) && get_class($response) == 'WP_Error') {
 | 
						|
 | 
						|
              $urlElements = parse_url($requestURL);
 | 
						|
              $portConnect = @fsockopen($urlElements['host'],8,$errno,$errstr,15);
 | 
						|
              if(!$portConnect) {
 | 
						|
                  $defaultData['Message'] .= "<BR>Debug info: <i>$errstr</i>";
 | 
						|
              }
 | 
						|
              return $defaultData;
 | 
						|
          }
 | 
						|
 | 
						|
          if($response['response']['code'] != 200) {
 | 
						|
             return $defaultData;
 | 
						|
          }
 | 
						|
 | 
						|
          $data = $response['body'];
 | 
						|
          $data = json_decode($data);
 | 
						|
 | 
						|
          if(empty($data)) { return $defaultData; }
 | 
						|
 | 
						|
          if($data->Status->Code != 2) {
 | 
						|
              $defaultData['Message'] = $data->Status->Message;
 | 
						|
              return $defaultData;
 | 
						|
          }
 | 
						|
 | 
						|
          $dataArray = array(
 | 
						|
              "APIKeyValid" => true,
 | 
						|
              "APICallsMade" => number_format($data->APICallsMade) . __(' credits','shortpixel-image-optimiser'),
 | 
						|
              "APICallsQuota" => number_format($data->APICallsQuota) . __(' credits','shortpixel-image-optimiser'),
 | 
						|
              "APICallsMadeOneTime" => number_format($data->APICallsMadeOneTime) . __(' credits','shortpixel-image-optimiser'),
 | 
						|
              "APICallsQuotaOneTime" => number_format($data->APICallsQuotaOneTime) . __(' credits','shortpixel-image-optimiser'),
 | 
						|
              "APICallsMadeNumeric" => (int) max($data->APICallsMade, 0),
 | 
						|
              "APICallsQuotaNumeric" => (int) max($data->APICallsQuota, 0),
 | 
						|
              "APICallsMadeOneTimeNumeric" =>  (int) max($data->APICallsMadeOneTime, 0),
 | 
						|
              "APICallsQuotaOneTimeNumeric" => (int) max($data->APICallsQuotaOneTime, 0),
 | 
						|
 | 
						|
              "Unlimited" => (property_exists($data, 'Unlimited') && $data->Unlimited == 'true') ? true : false,
 | 
						|
 | 
						|
              "APILastRenewalDate" => $data->DateSubscription,
 | 
						|
              "DomainCheck" => (isset($data->DomainCheck) ? $data->DomainCheck : null)
 | 
						|
          );
 | 
						|
					 // My Eyes!  Basically :  ApiCalls - ApiCalls used, both for monthly and onetime. Max of each is 0.  Negative quota seems possible, but should not be substracted from one or the other.
 | 
						|
					 $dataArray["APICallsRemaining"] = max($dataArray['APICallsQuotaNumeric'] - $dataArray['APICallsMadeNumeric'], 0) + max($dataArray['APICallsQuotaOneTimeNumeric'] - $dataArray['APICallsMadeOneTimeNumeric'],0);
 | 
						|
 | 
						|
					//reset quota exceeded flag -> user is allowed to process more images.
 | 
						|
 | 
						|
          if ( $dataArray['APICallsRemaining'] > 0 || $dataArray['Unlimited'])
 | 
						|
					{
 | 
						|
              $this->resetQuotaExceeded();
 | 
						|
					}
 | 
						|
          else
 | 
						|
					{
 | 
						|
							//activate quota limiting
 | 
						|
              $this->setQuotaExceeded();
 | 
						|
					}
 | 
						|
 | 
						|
      //    Log::addDebug('GetQuotaInformation Result ', $dataArray);
 | 
						|
          return $dataArray;
 | 
						|
    }
 | 
						|
 | 
						|
}
 |