. */ namespace Xibo\Xmds; use Carbon\Carbon; use Illuminate\Support\Str; use Stash\Invalidation; use Xibo\Entity\Bandwidth; use Xibo\Entity\Display; use Xibo\Helper\DateFormatHelper; use Xibo\Helper\Random; use Xibo\Support\Exception\GeneralException; use Xibo\Support\Exception\NotFoundException; /** * Class Soap5 * @package Xibo\Xmds * * @phpcs:disable PSR1.Methods.CamelCapsMethodName.NotCamelCaps */ class Soap5 extends Soap4 { /** * 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 string $xmrChannel * @param string $xmrPubKey * @param string $licenceCheck * @return string * @throws NotFoundException * @throws \SoapFault * @throws GeneralException */ public function RegisterDisplay( $serverKey, $hardwareKey, $displayName, $clientType, $clientVersion, $clientCode, $operatingSystem, $macAddress, $xmrChannel = null, $xmrPubKey = null, $licenceResult = 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, 'licenceResult' => $licenceResult ]); // 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(); $xmrChannel = $sanitized->getString('xmrChannel'); $xmrPubKey = trim($sanitized->getString('xmrPubKey')); $operatingSystem = $sanitized->getString('operatingSystem'); // this is only sent from xmds v7 $commercialLicenceString = $sanitized->getString('licenceResult'); if ($xmrPubKey != '' && !Str::contains($xmrPubKey, 'BEGIN PUBLIC KEY')) { $xmrPubKey = "-----BEGIN PUBLIC KEY-----\n" . $xmrPubKey . "\n-----END PUBLIC KEY-----\n"; } // 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); // Uncomment this if we want additional logging in register. //$this->logProcessor->setDisplay(0, 'debug'); // 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 Registered and awaiting Authorisation from an Administrator in the CMS' ); } 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']; // Disable the CEF browser option on Windows players if (strtolower($settingName) == 'usecefwebbrowser' && ($clientType == 'windows')) { $arrayItem['value'] = 0; } // Override the XMR address if empty if (strtolower($settingName) == 'xmrnetworkaddress' && (!isset($arrayItem['value']) || $arrayItem['value'] == '') ) { $arrayItem['value'] = $this->getConfig()->getSetting('XMR_PUB_ADDRESS'); } // Override the XMR address if empty if (strtolower($settingName) == 'xmrwebsocketaddress' && (!isset($arrayItem['value']) || $arrayItem['value'] == '') ) { $arrayItem['value'] = $this->getConfig()->getSetting('XMR_WS_ADDRESS'); } // logLevels if (strtolower($settingName) == 'loglevel') { // return resting log level // unless it is currently elevated, in which case return debug $arrayItem['value'] = $this->display->getLogLevel(); } $value = ($arrayItem['value'] ?? $arrayItem['default']); // Patch download and update windows to make sure they are only 00:00 // 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(':', $value); $value = $timeParts[0] . ':' . $timeParts[1]; } // Apply an offset to the collectInterval // https://github.com/xibosignage/xibo/issues/3530 if (strtolower($arrayItem['name']) == 'collectinterval') { $value = $this->collectionIntervalWithOffset($value); } $node = $return->createElement($settingName, $value); if (isset($arrayItem['type'])) { $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); // cms move $displayMoveAddress = ($clientType == 'windows') ? 'NewCmsAddress' : 'newCmsAddress'; $node = $return->createElement($displayMoveAddress, $display->newCmsAddress); if ($clientType == 'windows') { $node->setAttribute('type', 'string'); } $displayElement->appendChild($node); $displayMoveKey = ($clientType == 'windows') ? 'NewCmsKey' : 'newCmsKey'; $node = $return->createElement($displayMoveKey, $display->newCmsKey); if ($clientType == 'windows') { $node->setAttribute('type', 'string'); } $displayElement->appendChild($node); // Add some special settings $nodeName = ($clientType == 'windows') ? 'DisplayName' : 'displayName'; $node = $return->createElement($nodeName); $node->appendChild($return->createTextNode($display->display)); if ($clientType == 'windows') { $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 : ''); if ($clientType == 'windows') { $node->setAttribute('type', 'string'); } $displayElement->appendChild($node); // Adspace Enabled CMS? $isAdspaceEnabled = intval($this->getConfig()->getSetting('isAdspaceEnabled', 0)); $node = $return->createElement('isAdspaceEnabled', $isAdspaceEnabled); $node->setAttribute('type', 'checkbox'); $displayElement->appendChild($node); if (!empty($display->timeZone)) { // Calculate local time $dateNow->timezone($display->timeZone); // Append Local Time $displayElement->setAttribute('localTimezone', $display->timeZone); $displayElement->setAttribute('localDate', $dateNow->format(DateFormatHelper::getSystemFormat())); } // XMR // Type of XMR connection $node = $return->createElement('xmrType', $this->display->isWebSocketXmrSupported() ? 'ws' : 'zmq'); $node->setAttribute('type', 'string'); $displayElement->appendChild($node); // XMR key (this is the key a player should use the intialise a connection to XMR $node = $return->createElement('xmrCmsKey', $this->getConfig()->getSetting('XMR_CMS_KEY')); $node->setAttribute('type', 'string'); $displayElement->appendChild($node); // Commands $commands = $display->getCommands(); if (count($commands) > 0) { // Append a command element $commandElement = $return->createElement('commands'); $displayElement->appendChild($commandElement); // Append each individual command foreach ($display->getCommands() as $command) { try { if (!$command->isReady()) { continue; } $node = $return->createElement($command->code); $commandString = $return->createElement('commandString'); $commandStringCData = $return->createCDATASection($command->getCommandString()); $commandString->appendChild($commandStringCData); $validationString = $return->createElement('validationString'); $validationStringCData = $return->createCDATASection($command->getValidationString()); $validationString->appendChild($validationStringCData); $alertOnString = $return->createElement('createAlertOn'); $alertOnStringCData = $return->createCDATASection($command->getCreateAlertOn()); $alertOnString->appendChild($alertOnStringCData); $node->appendChild($commandString); $node->appendChild($validationString); $node->appendChild($alertOnString); $commandElement->appendChild($node); } catch (\DOMException $DOMException) { $this->getLog()->error( 'Cannot add command to settings for displayId ' . $this->display->displayId . ', ' . $DOMException->getMessage() ); } } } // Tags if (count($display->tags) > 0) { $tagsElement = $return->createElement('tags'); $displayElement->appendChild($tagsElement); foreach ($display->tags as $tagLink) { $tagNode = $return->createElement('tag'); $tagNameNode = $return->createElement('tagName'); $tag = $return->createTextNode($tagLink->tag); $tagNameNode->appendChild($tag); $tagNode->appendChild($tagNameNode); if ($tagLink->value !== null) { $valueNode = $return->createElement('tagValue'); $value = $return->createTextNode($tagLink->value); $valueNode->appendChild($value); $tagNode->appendChild($valueNode); } $tagsElement->appendChild($tagNode); } } // Check to see if the channel/pubKey are already entered $this->getLog()->debug('xmrChannel: [' . $xmrChannel . ']. xmrPublicKey: [' . $xmrPubKey . ']'); // Update the Channel $display->xmrChannel = $xmrChannel; // Update the PUB Key only if it has been cleared if ($display->xmrPubKey == '') { $display->xmrPubKey = $xmrPubKey; } } } catch (NotFoundException $e) { // Add a new display try { $display = $this->displayFactory->createEmpty(); $this->display = $display; $display->display = $displayName; $display->auditingUntil = 0; // defaultLayoutId column cannot be null or empty string // if we do not have global default layout, set it here to 0 to allow register to proceed $display->defaultLayoutId = intval($this->getConfig()->getSetting('DEFAULT_LAYOUT', 0)); $display->license = $hardwareKey; $display->licensed = $this->getConfig()->getSetting('DISPLAY_AUTO_AUTH', 0); $display->incSchedule = 0; $display->clientAddress = $this->getIp(); $display->xmrChannel = $xmrChannel; $display->xmrPubKey = $xmrPubKey; $display->folderId = $this->getConfig()->getSetting('DISPLAY_DEFAULT_FOLDER', 1); 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 is now Registered and awaiting Authorisation from an Administrator in the CMS' ); } 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 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; // Commercial Licence Check, 0 - Not licensed, 1 - licensed, 2 - trial licence, 3 - not applicable // only sent by xmds v7 if (!empty($commercialLicenceString) && !in_array($display->clientType, ['windows', 'linux'])) { $commercialLicenceString = strtolower($commercialLicenceString); if ($commercialLicenceString === 'licensed' || $commercialLicenceString === 'full') { $commercialLicence = 1; } elseif ($commercialLicenceString === 'trial') { $commercialLicence = 2; } else { $commercialLicence = 0; } $display->commercialLicence = $commercialLicence; $node = $return->createElement('commercialLicence', $commercialLicenceString); $displayElement->appendChild($node); } // commercial licence not applicable for Windows and Linux players. if (in_array($display->clientType, ['windows', 'linux'])) { $display->commercialLicence = 3; } if (!empty($display->syncGroupId)) { try { $syncGroup = $this->syncGroupFactory->getById($display->syncGroupId); if ($syncGroup->leadDisplayId === $display->displayId) { $syncNodeValue = 'lead'; } else { $leadDisplay = $this->syncGroupFactory->getLeadDisplay($syncGroup->leadDisplayId); $syncNodeValue = $leadDisplay->lanIpAddress; } $syncNode = $return->createElement('syncGroup'); $value = $return->createTextNode($syncNodeValue ?? ''); $syncNode->appendChild($value); $displayElement->appendChild($syncNode); $syncPublisherPortNode = $return->createElement('syncPublisherPort'); $value = $return->createTextNode($syncGroup->syncPublisherPort ?? 9590); $syncPublisherPortNode->appendChild($value); $displayElement->appendChild($syncPublisherPortNode); $syncSwitchDelayNode = $return->createElement('syncSwitchDelay'); $value = $return->createTextNode($syncGroup->syncSwitchDelay ?? 750); $syncSwitchDelayNode->appendChild($value); $displayElement->appendChild($syncSwitchDelayNode); $syncVideoPauseDelayNode = $return->createElement('syncVideoPauseDelay'); $value = $return->createTextNode($syncGroup->syncVideoPauseDelay ?? 100); $syncVideoPauseDelayNode->appendChild($value); $displayElement->appendChild($syncVideoPauseDelayNode); } catch (GeneralException $exception) { $this->getLog()->error('registerDisplay: cannot get sync group information, e = ' . $exception->getMessage()); } } $display->save(Display::$saveOptionsMinimum); // cache checks $cacheSchedule = $this->getPool()->getItem($this->display->getCacheKey() . '/schedule'); $cacheSchedule->setInvalidationMethod(Invalidation::OLD); $displayElement->setAttribute( 'checkSchedule', ($cacheSchedule->isHit() ? crc32($cacheSchedule->get()) : '') ); $cacheRF = $this->getPool()->getItem($this->display->getCacheKey() . '/requiredFiles'); $cacheRF->setInvalidationMethod(Invalidation::OLD); $displayElement->setAttribute('checkRf', ($cacheRF->isHit() ? crc32($cacheRF->get()) : '')); // Log Bandwidth $returnXml = $return->saveXML(); $this->logBandwidth($display->displayId, Bandwidth::$REGISTER, strlen($returnXml)); // Audit our return $this->getLog()->debug($returnXml); return $returnXml; } /** * Returns the schedule for the hardware key specified * @param string $serverKey * @param string $hardwareKey * @return string * @throws NotFoundException * @throws \SoapFault */ public function Schedule($serverKey, $hardwareKey) { return $this->doSchedule($serverKey, $hardwareKey, ['dependentsAsNodes' => true, 'includeOverlays' => true]); } }