Files
Cloud-CMS/lib/Xmds/Soap4.php
Matt Batchelder 05ce0da296 Initial Upload
2025-12-02 10:32:59 -05:00

829 lines
33 KiB
PHP

<?php
/*
* Copyright (C) 2025 Xibo Signage Ltd
*
* Xibo - Digital Signage - https://xibosignage.com
*
* This file is part of Xibo.
*
* Xibo is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* Xibo 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 Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with Xibo. If not, see <http://www.gnu.org/licenses/>.
*/
namespace Xibo\Xmds;
use Carbon\Carbon;
use Intervention\Image\ImageManagerStatic as Img;
use Xibo\Entity\Bandwidth;
use Xibo\Entity\Display;
use Xibo\Event\XmdsDependencyRequestEvent;
use Xibo\Helper\DateFormatHelper;
use Xibo\Helper\Random;
use Xibo\Support\Exception\GeneralException;
use Xibo\Support\Exception\NotFoundException;
/**
* Class Soap4
* @package Xibo\Xmds
* @phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps
*/
class Soap4 extends Soap
{
/**
* Registers a new display
* @param string $serverKey
* @param string $hardwareKey
* @param string $displayName
* @param string $clientType
* @param string $clientVersion
* @param int $clientCode
* @param string $operatingSystem
* @param string $macAddress
* @param null $xmrChannel
* @param null $xmrPubKey
* @return string
* @throws GeneralException
* @throws NotFoundException
* @throws \SoapFault
*/
public function RegisterDisplay($serverKey, $hardwareKey, $displayName, $clientType, $clientVersion, $clientCode, $operatingSystem, $macAddress, $xmrChannel = null, $xmrPubKey = null)
{
$this->logProcessor->setRoute('RegisterDisplay');
$sanitized = $this->getSanitizer([
'serverKey' => $serverKey,
'hardwareKey' => $hardwareKey,
'displayName' => $displayName,
'clientType' => $clientType,
'clientVersion' => $clientVersion,
'clientCode' => $clientCode,
'operatingSystem' => $operatingSystem,
'macAddress' => $macAddress,
'xmrChannel' => $xmrChannel,
'xmrPubKey' => $xmrPubKey,
]);
// Sanitize
$serverKey = $sanitized->getString('serverKey');
$hardwareKey = $sanitized->getString('hardwareKey');
$displayName = $sanitized->getString('displayName');
$clientType = $sanitized->getString('clientType');
$clientVersion = $sanitized->getString('clientVersion');
$clientCode = $sanitized->getInt('clientCode');
$macAddress = $sanitized->getString('macAddress');
$clientAddress = $this->getIp();
$operatingSystem = $sanitized->getString('operatingSystem');
// Check the serverKey matches
if ($serverKey != $this->getConfig()->getSetting('SERVER_KEY')) {
throw new \SoapFault('Sender', 'The Server key you entered does not match with the server key at this address');
}
// Check the Length of the hardwareKey
if (strlen($hardwareKey) > 40) {
throw new \SoapFault('Sender', 'The Hardware Key you sent was too long. Only 40 characters are allowed (SHA1).');
}
// Return an XML formatted string
$return = new \DOMDocument('1.0');
$displayElement = $return->createElement('display');
$return->appendChild($displayElement);
// Check in the database for this hardwareKey
try {
$display = $this->displayFactory->getByLicence($hardwareKey);
$this->display = $display;
$this->logProcessor->setDisplay($display->displayId, $display->isAuditing());
// Audit in
$this->getLog()->debug(
'serverKey: ' . $serverKey . ', hardwareKey: ' . $hardwareKey .
', displayName: ' . $displayName . ', macAddress: ' . $macAddress
);
// Now
$dateNow = Carbon::now();
// Append the time
$displayElement->setAttribute('date', $dateNow->format(DateFormatHelper::getSystemFormat()));
$displayElement->setAttribute('timezone', $this->getConfig()->getSetting('defaultTimezone'));
// Determine if we are licensed or not
if ($display->licensed == 0) {
// It is not authorised
$displayElement->setAttribute('status', 2);
$displayElement->setAttribute('code', 'WAITING');
$displayElement->setAttribute('message', 'Display is awaiting licensing approval from an Administrator.');
} else {
// It is licensed
$displayElement->setAttribute('status', 0);
$displayElement->setAttribute('code', 'READY');
$displayElement->setAttribute('message', 'Display is active and ready to start.');
// Display Settings
$settings = $this->display->getSettings(['displayOverride' => true]);
// Create the XML nodes
foreach ($settings as $arrayItem) {
// Upper case the setting name for windows
$settingName = ($clientType == 'windows') ? ucfirst($arrayItem['name']) : $arrayItem['name'];
// Patch download and update windows to make sure they are unix time stamps
// XMDS schema 4 sent down unix time
// https://github.com/xibosignage/xibo/issues/1791
if (strtolower($arrayItem['name']) == 'downloadstartwindow'
|| strtolower($arrayItem['name']) == 'downloadendwindow'
|| strtolower($arrayItem['name']) == 'updatestartwindow'
|| strtolower($arrayItem['name']) == 'updateendwindow'
) {
// Split by :
$timeParts = explode(':', $arrayItem['value']);
if ($timeParts[0] == '00' && $timeParts[1] == '00') {
$arrayItem['value'] = 0;
} else {
$arrayItem['value'] = Carbon::now()->setTime(intval($timeParts[0]), intval($timeParts[1]));
}
}
// Apply an offset to the collectInterval
// https://github.com/xibosignage/xibo/issues/3530
if (strtolower($arrayItem['name']) == 'collectinterval') {
$arrayItem['value'] = $this->collectionIntervalWithOffset($arrayItem['value']);
}
$node = $return->createElement($arrayItem['name'], $arrayItem['value'] ?? $arrayItem['default']);
$node->setAttribute('type', $arrayItem['type']);
$displayElement->appendChild($node);
}
// Player upgrades
$version = '';
try {
$versionId = $this->display->getSetting('versionMediaId', null, ['displayOverride' => true]);
if ($clientType != 'windows' && $versionId != null) {
$version = $this->playerVersionFactory->getById($versionId);
if ($clientType == 'android') {
$version = json_encode([
'id' => $versionId,
'file' => $version->fileName,
'code' => $version->code
]);
} elseif ($clientType == 'lg') {
$version = json_encode([
'id' => $versionId,
'file' => $version->fileName,
'code' => $version->code
]);
} elseif ($clientType == 'sssp') {
// Create a nonce and store it in the cache for this display.
$nonce = Random::generateString();
$cache = $this->getPool()->getItem('/playerVersion/' . $nonce);
$cache->set($this->display->displayId);
$cache->expiresAfter(86400);
$this->getPool()->saveDeferred($cache);
$version = json_encode([
'id' => $versionId,
'file' => $version->fileName,
'code' => $version->code,
'url' => str_replace('/xmds.php', '', Wsdl::getRoot()) . '/playersoftware/' . $nonce
]);
}
}
} catch (NotFoundException $notFoundException) {
$this->getLog()->error('Non-existing version set on displayId ' . $this->display->displayId);
}
$displayElement->setAttribute('version_instructions', $version);
// Add some special settings
$nodeName = ($clientType == 'windows') ? 'DisplayName' : 'displayName';
$node = $return->createElement($nodeName);
$node->appendChild($return->createTextNode($display->display));
$node->setAttribute('type', 'string');
$displayElement->appendChild($node);
$nodeName = ($clientType == 'windows') ? 'ScreenShotRequested' : 'screenShotRequested';
$node = $return->createElement($nodeName, $display->screenShotRequested);
$node->setAttribute('type', 'checkbox');
$displayElement->appendChild($node);
$nodeName = ($clientType == 'windows') ? 'DisplayTimeZone' : 'displayTimeZone';
$node = $return->createElement($nodeName, (!empty($display->timeZone)) ? $display->timeZone : '');
$node->setAttribute('type', 'string');
$displayElement->appendChild($node);
if (!empty($display->timeZone)) {
// Calculate local time
$dateNow->timezone($display->timeZone);
// Append Local Time
$displayElement->setAttribute('localDate', $dateNow->format(DateFormatHelper::getSystemFormat()));
}
}
} catch (NotFoundException $e) {
// Add a new display
try {
$display = $this->displayFactory->createEmpty();
$this->display = $display;
$display->display = $displayName;
$display->auditingUntil = 0;
$display->defaultLayoutId = $this->getConfig()->getSetting('DEFAULT_LAYOUT');
$display->license = $hardwareKey;
$display->licensed = $this->getConfig()->getSetting('DISPLAY_AUTO_AUTH', 0);
$display->incSchedule = 0;
$display->clientAddress = $this->getIp();
if (!$display->isDisplaySlotAvailable()) {
$display->licensed = 0;
}
} catch (\InvalidArgumentException $e) {
throw new \SoapFault('Sender', $e->getMessage());
}
$displayElement->setAttribute('status', 1);
$displayElement->setAttribute('code', 'ADDED');
if ($display->licensed == 0) {
$displayElement->setAttribute('message', 'Display added and is awaiting licensing approval from an Administrator.');
} else {
$displayElement->setAttribute('message', 'Display is active and ready to start.');
}
}
// Send Notification if required
$this->alertDisplayUp();
$display->lastAccessed = Carbon::now()->format('U');
$display->loggedIn = 1;
$display->clientAddress = $clientAddress;
$display->macAddress = $macAddress;
$display->clientType = $clientType;
$display->clientVersion = $clientVersion;
$display->clientCode = $clientCode;
// Parse operatingSystem JSON data
$operatingSystemJson = json_decode($operatingSystem, false);
// Newer version of players will return a JSON value, but for older version, it will return a string.
// In case the json decode fails, use the operatingSystem string value as the default value for the osVersion.
$display->osVersion = $operatingSystemJson->version ?? $operatingSystem;
$display->osSdk = $operatingSystemJson->sdk ?? null;
$display->manufacturer = $operatingSystemJson->manufacturer ?? null;
$display->brand = $operatingSystemJson->brand ?? null;
$display->model = $operatingSystemJson->model ?? null;
$display->save(['validate' => false, 'audit' => false]);
// Log Bandwidth
$returnXml = $return->saveXML();
$this->logBandwidth($display->displayId, Bandwidth::$REGISTER, strlen($returnXml));
// Audit our return
$this->getLog()->debug($returnXml);
return $returnXml;
}
/**
* Returns a string containing the required files xml for the requesting display
* @param string $serverKey The Server Key
* @param string $hardwareKey Display Hardware Key
* @return string $requiredXml Xml Formatted String
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function RequiredFiles($serverKey, $hardwareKey)
{
$httpDownloads = ($this->getConfig()->getSetting('SENDFILE_MODE') != 'Off');
return $this->doRequiredFiles($serverKey, $hardwareKey, $httpDownloads);
}
/**
* Get File
* @param string $serverKey The ServerKey for this CMS
* @param string $hardwareKey The HardwareKey for this Display
* @param int $fileId The ID
* @param string $fileType The File Type
* @param int $chunkOffset The Offset of the Chunk Requested
* @param string $chunkSize The Size of the Chunk Requested
* @return mixed
* @throws \SoapFault
* @throws GeneralException
* @throws \Xibo\Support\Exception\InvalidArgumentException
* @throws \Xibo\Support\Exception\NotFoundException
*/
function GetFile($serverKey, $hardwareKey, $fileId, $fileType, $chunkOffset, $chunkSize, $isDependency = false)
{
$this->logProcessor->setRoute('GetFile');
$sanitizer = $this->getSanitizer([
'serverKey' => $serverKey,
'hardwareKey' => $hardwareKey,
'fileId' => $fileId,
'fileType' => $fileType,
'chunkOffset' => $chunkOffset,
'chunkSize' => $chunkSize
]);
// Sanitize
$serverKey = $sanitizer->getString('serverKey');
$hardwareKey = $sanitizer->getString('hardwareKey');
if ($isDependency) {
$fileId = $sanitizer->getString('fileId');
} else {
$fileId = $sanitizer->getInt('fileId');
}
$fileType = $sanitizer->getString('fileType');
$chunkOffset = $sanitizer->getDouble('chunkOffset');
$chunkSize = $sanitizer->getDouble('chunkSize');
$libraryLocation = $this->getConfig()->getSetting('LIBRARY_LOCATION');
// Check the serverKey matches
if ($serverKey != $this->getConfig()->getSetting('SERVER_KEY')) {
throw new \SoapFault(
'Sender',
'The Server key you entered does not match with the server key at this address'
);
}
// Authenticate this request...
if (!$this->authDisplay($hardwareKey)) {
throw new \SoapFault('Receiver', 'This Display is not authorised.');
}
// Now that we authenticated the Display, make sure we are sticking to our bandwidth limit
if (!$this->checkBandwidth($this->display->displayId)) {
throw new \SoapFault('Receiver', 'Bandwidth Limit exceeded');
}
$this->getLog()->debug(
'hardwareKey: ' . $hardwareKey . ', fileId: ' . $fileId . ', fileType: ' . $fileType .
', chunkOffset: ' . $chunkOffset . ', chunkSize: ' . $chunkSize
);
try {
if ($isDependency || ($fileType == 'media' && $fileId < 0)) {
// Validate the nonce
// If we are an older player downloading as media using a faux fileId, then this lookup
// should be performed against the `itemId`
$requiredFile = $this->requiredFileFactory->getByDisplayAndDependency(
$this->display->displayId,
$fileType,
$fileId,
!($fileType == 'media' && $fileId < 0)
);
// File is valid, see if we can return it.
$event = new XmdsDependencyRequestEvent($requiredFile);
$this->getDispatcher()->dispatch($event, 'xmds.dependency.request');
// Get the path
$path = $event->getRelativePath();
if (empty($path)) {
throw new NotFoundException(__('File not found'));
}
$path = $libraryLocation . $path;
$f = fopen($path, 'r');
if (!$f) {
throw new NotFoundException(__('Unable to get file pointer'));
}
fseek($f, $chunkOffset);
$file = fread($f, $chunkSize);
// Store file size for bandwidth log
$chunkSize = strlen($file);
if ($chunkSize === 0) {
throw new NotFoundException(__('Empty file'));
}
$requiredFile->bytesRequested = $requiredFile->bytesRequested + $chunkSize;
$requiredFile->save();
} else if ($fileType == 'layout') {
// Validate the nonce
$requiredFile = $this->requiredFileFactory->getByDisplayAndLayout($this->display->displayId, $fileId);
// Load the layout
$layout = $this->layoutFactory->concurrentRequestLock($this->layoutFactory->getById($fileId));
try {
$path = $layout->xlfToDisk();
} finally {
$this->layoutFactory->concurrentRequestRelease($layout);
}
$file = file_get_contents($path);
$chunkSize = filesize($path);
$requiredFile->bytesRequested = $requiredFile->bytesRequested + $chunkSize;
$requiredFile->save();
} else if ($fileType == 'media') {
// A normal media file.
$requiredFile = $this->requiredFileFactory->getByDisplayAndMedia(
$this->display->displayId,
$fileId
);
$media = $this->mediaFactory->getById($fileId);
$this->getLog()->debug(json_encode($media));
if (!file_exists($libraryLocation . $media->storedAs)) {
throw new NotFoundException(__('Media exists but file missing from library.'));
}
// Return the Chunk size specified
$f = fopen($libraryLocation . $media->storedAs, 'r');
if (!$f) {
throw new NotFoundException(__('Unable to get file pointer'));
}
fseek($f, $chunkOffset);
$file = fread($f, $chunkSize);
// Store file size for bandwidth log
$chunkSize = strlen($file);
if ($chunkSize === 0) {
throw new NotFoundException(__('Empty file'));
}
$requiredFile->bytesRequested = $requiredFile->bytesRequested + $chunkSize;
$requiredFile->save();
} else {
throw new NotFoundException(__('Unknown FileType Requested.'));
}
} catch (NotFoundException $e) {
$this->getLog()->error('Not found FileId: ' . $fileId . '. FileType: '
. $fileType . '. ' . $e->getMessage());
throw new \SoapFault('Receiver', 'Requested an invalid file.');
}
// Log Bandwidth
if ($isDependency) {
$this->logBandwidth($this->display->displayId, Bandwidth::$GET_DEPENDENCY, $chunkSize);
} else {
$this->logBandwidth($this->display->displayId, Bandwidth::$GETFILE, $chunkSize);
}
return $file;
}
/**
* Returns the schedule for the hardware key specified
* @param string $serverKey
* @param string $hardwareKey
* @return string
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function Schedule($serverKey, $hardwareKey)
{
return $this->doSchedule($serverKey, $hardwareKey);
}
/**
* Black List
* @param string $serverKey
* @param string $hardwareKey
* @param string $mediaId
* @param string $type
* @param string $reason
* @return bool
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function BlackList($serverKey, $hardwareKey, $mediaId, $type, $reason)
{
return $this->doBlackList($serverKey, $hardwareKey, $mediaId, $type, $reason);
}
/**
* Submit client logging
* @param string $serverKey
* @param string $hardwareKey
* @param string $logXml
* @return bool
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function SubmitLog($serverKey, $hardwareKey, $logXml)
{
return $this->doSubmitLog($serverKey, $hardwareKey, $logXml);
}
/**
* Submit display statistics to the server
* @param string $serverKey
* @param string $hardwareKey
* @param string $statXml
* @return bool
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function SubmitStats($serverKey, $hardwareKey, $statXml)
{
return $this->doSubmitStats($serverKey, $hardwareKey, $statXml);
}
/**
* Store the media inventory for a client
* @param string $serverKey
* @param string $hardwareKey
* @param string $inventory
* @return bool
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function MediaInventory($serverKey, $hardwareKey, $inventory)
{
return $this->doMediaInventory($serverKey, $hardwareKey, $inventory);
}
/**
* Gets additional resources for assigned media
* @param string $serverKey
* @param string $hardwareKey
* @param int $layoutId
* @param string $regionId
* @param string $mediaId
* @return mixed
* @throws \SoapFault
* @throws \Xibo\Support\Exception\NotFoundException
*/
function GetResource($serverKey, $hardwareKey, $layoutId, $regionId, $mediaId)
{
return $this->doGetResource($serverKey, $hardwareKey, $layoutId, $regionId, $mediaId);
}
/**
* Notify Status
* @param string $serverKey
* @param string $hardwareKey
* @param string $status
* @return bool
* @throws \SoapFault
* @throws GeneralException
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function NotifyStatus($serverKey, $hardwareKey, $status)
{
$this->logProcessor->setRoute('NotifyStatus');
$sanitizer = $this->getSanitizer([
'serverKey' => $serverKey,
'hardwareKey' => $hardwareKey
]);
// Sanitize
$serverKey = $sanitizer->getString('serverKey');
$hardwareKey = $sanitizer->getString('hardwareKey');
// Check the serverKey matches
if ($serverKey != $this->getConfig()->getSetting('SERVER_KEY')) {
throw new \SoapFault('Sender', 'The Server key you entered does not match with the server key at this address');
}
// Auth this request...
if (!$this->authDisplay($hardwareKey)) {
throw new \SoapFault('Receiver', 'This Display is not authorised.');
}
// Now that we authenticated the Display, make sure we are sticking to our bandwidth limit
if (!$this->checkBandwidth($this->display->displayId)) {
throw new \SoapFault('Receiver', "Bandwidth Limit exceeded");
}
// Important to keep this logging in place (status screen notification gets logged)
$this->getLog()->debug($status);
$this->logBandwidth($this->display->displayId, Bandwidth::$NOTIFYSTATUS, strlen($status));
$status = json_decode($status, true);
$sanitizedStatus = $this->getSanitizer($status);
$this->display->storageAvailableSpace = $sanitizedStatus->getInt('availableSpace', ['default' => $this->display->storageAvailableSpace]);
$this->display->storageTotalSpace = $sanitizedStatus->getInt('totalSpace', ['default' => $this->display->storageTotalSpace]);
$this->display->lastCommandSuccess = $sanitizedStatus->getCheckbox('lastCommandSuccess');
$this->display->deviceName = $sanitizedStatus->getString('deviceName', ['default' => $this->display->deviceName]);
$this->display->lanIpAddress = $sanitizedStatus->getString('lanIpAddress', ['default' => $this->display->lanIpAddress]);
$commercialLicenceString = $sanitizedStatus->getString('licenceResult', ['default' => null]);
// Commercial Licence Check, 0 - Not licensed, 1 - licensed, 2 - trial licence, 3 - not applicable
if (!empty($commercialLicenceString)) {
if ($commercialLicenceString === 'Licensed fully'
|| $commercialLicenceString === 'licensed'
|| $commercialLicenceString === 'full'
) {
$commercialLicence = 1;
} elseif ($commercialLicenceString === 'Trial' || $commercialLicenceString === 'trial') {
$commercialLicence = 2;
} else {
$commercialLicence = 0;
}
$this->display->commercialLicence = $commercialLicence;
}
// commercial licence not applicable for Windows and Linux players.
if (in_array($this->display->clientType, ['windows', 'linux'])) {
$this->display->commercialLicence = 3;
}
if ($this->getConfig()->getSetting('DISPLAY_LOCK_NAME_TO_DEVICENAME') == 1 && $this->display->hasPropertyChanged('deviceName')) {
$this->display->display = $this->display->deviceName;
}
// Timezone
$timeZone = $sanitizedStatus->getString('timeZone');
if (!empty($timeZone)) {
// Validate the provided data and log/ignore if not well formatted
if (array_key_exists($timeZone, DateFormatHelper::timezoneList())) {
$this->display->timeZone = $timeZone;
} else {
$this->getLog()->info('Ignoring Incorrect timezone string: ' . $timeZone);
}
}
// Current Layout
// don't fail: xibosignage/xibo#2517
try {
$currentLayoutId = $sanitizedStatus->getInt('currentLayoutId');
if ($currentLayoutId !== null) {
$this->display->setCurrentLayoutId($this->getPool(), $currentLayoutId);
}
} catch (\Exception $exception) {
$this->getLog()->debug('Ignoring currentLayout due to a validation error.');
}
// Status Dialog
$statusDialog = $sanitizedStatus->getString('statusDialog', ['default' => null]);
if ($statusDialog !== null) {
// special handling for Android Players (Other Players send status as json already)
if ($this->display->clientType == 'android') {
$statusDialog = json_encode($statusDialog);
}
// Log in as an alert
$this->getLog()->alert($statusDialog);
// Cache on the display as transient data
try {
$this->display->setStatusWindow($this->getPool(), json_decode($statusDialog, true));
} catch (\Exception $exception) {
$this->getLog()->error('Unable to cache display status. e = ' . $exception->getMessage());
}
}
// Resolution
$width = $sanitizedStatus->getInt('width');
$height = $sanitizedStatus->getInt('height');
if ($width != null && $height != null) {
// Determine the orientation
$this->display->orientation = ($width >= $height) ? 'landscape' : 'portrait';
$this->display->resolution = $width . 'x' . $height;
}
// Lat/Long
$latitude = $sanitizedStatus->getDouble('latitude', ['default' => null]);
$longitude = $sanitizedStatus->getDouble('longitude', ['default' => null]);
if ($latitude != null && $longitude != null) {
$this->display->latitude = $latitude;
$this->display->longitude = $longitude;
}
// Touch the display record
try {
if (count($this->display->getChangedProperties()) > 0) {
$this->display->save(Display::$saveOptionsMinimum);
}
} catch (GeneralException $xiboException) {
$this->getLog()->error($xiboException->getMessage());
throw new \SoapFault('Receiver', 'Unable to save status update');
}
return true;
}
/**
* Submit ScreenShot
* @param string $serverKey
* @param string $hardwareKey
* @param string $screenShot
* @return bool
* @throws \SoapFault
* @throws GeneralException
* @throws \Xibo\Support\Exception\NotFoundException
*/
public function SubmitScreenShot($serverKey, $hardwareKey, $screenShot)
{
$this->logProcessor->setRoute('SubmitScreenShot');
$sanitizer = $this->getSanitizer([
'serverKey' => $serverKey,
'hardwareKey' => $hardwareKey,
]);
// Sanitize
$serverKey = $sanitizer->getString('serverKey');
$hardwareKey = $sanitizer->getString('hardwareKey');
$screenShotFmt = "jpg";
$screenShotMime = "image/jpeg";
$screenShotImg = false;
$converted = false;
$needConversion = false;
// Check the serverKey matches
if ($serverKey != $this->getConfig()->getSetting('SERVER_KEY')) {
throw new \SoapFault('Sender', 'The Server key you entered does not match with the server key at this address');
}
// Auth this request...
if (!$this->authDisplay($hardwareKey)) {
throw new \SoapFault('Receiver', 'This Display is not authorised.');
}
// Now that we authenticated the Display, make sure we are sticking to our bandwidth limit
if (!$this->checkBandwidth($this->display->displayId)) {
throw new \SoapFault('Receiver', "Bandwidth Limit exceeded");
}
$this->getLog()->debug('Received Screen shot');
// Open this displays screen shot file and save this.
$location = $this->getConfig()->getSetting('LIBRARY_LOCATION') . 'screenshots/' . $this->display->displayId . '_screenshot.' . $screenShotFmt;
foreach (array('imagick', 'gd') as $imgDriver) {
Img::configure(array('driver' => $imgDriver));
try {
$screenShotImg = Img::make($screenShot);
} catch (\Exception $e) {
$this->getLog()->debug($imgDriver . ' - ' . $e->getMessage());
}
if ($screenShotImg !== false) {
$this->getLog()->debug('Use ' . $imgDriver);
break;
}
}
if ($screenShotImg !== false) {
$imgMime = $screenShotImg->mime();
if ($imgMime != $screenShotMime) {
$needConversion = true;
try {
$this->getLog()->debug("converting: '" . $imgMime . "' to '" . $screenShotMime . "'");
$screenShot = (string) $screenShotImg->encode($screenShotFmt);
$converted = true;
} catch (\Exception $e) {
$this->getLog()->debug($e->getMessage());
}
}
}
// return early with false, keep screenShotRequested intact, let the Player retry.
if ($needConversion && !$converted) {
$this->logBandwidth($this->display->displayId, Bandwidth::$SCREENSHOT, filesize($location));
throw new \SoapFault('Receiver', __('Incorrect Screen shot Format'));
}
$fp = fopen($location, 'wb');
fwrite($fp, $screenShot);
fclose($fp);
// Touch the display record
$this->display->screenShotRequested = 0;
$this->display->save(Display::$saveOptionsMinimum);
// Cache the current screen shot time
$this->display->setCurrentScreenShotTime($this->getPool(), Carbon::now()->format(DateFormatHelper::getSystemFormat()));
$this->logBandwidth($this->display->displayId, Bandwidth::$SCREENSHOT, filesize($location));
return true;
}
}