403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/messageservice/lib/sender/sms/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/rospirotorg.ru/bitrix/modules/messageservice/lib/sender/sms/ednaruimhpx.php
<?php

namespace Bitrix\MessageService\Sender\Sms;

use Bitrix\Main\Error;
use Bitrix\Main\Result;
use Bitrix\Main\Type\Collection;
use Bitrix\Main\Web\HttpClient;
use Bitrix\Main\Web\Json;
use Bitrix\MessageService\DTO;
use Bitrix\MessageService\Message;
use Bitrix\MessageService\MessageStatus;
use Bitrix\MessageService\Sender;

class EdnaruImHpx extends Sender\BaseConfigurable
{
	public const ID = 'ednaruimhpx';

	protected const MESSAGE_TYPE = "whatsapp";
	protected const DEFAULT_PRIORITY = "normal";
	protected const CONTENT_TEXT = "text";

	private const ENDPOINT_OPTION = 'connector_endpoint';
	private const LOGIN_OPTION = 'login';
	private const PASSWORD_OPTION = 'password';
	private const SUBJECT_OPTION = 'subject_id';

	public static function isSupported()
	{
		return defined('MESSAGESERIVICE_ALLOW_IMHPX') && MESSAGESERIVICE_ALLOW_IMHPX === true;
	}

	public function getId()
	{
		return static::ID;
	}

	public function getName()
	{
		return 'Edna.ru IMHPX';
	}

	public function getShortName()
	{
		return 'Edna.ru IMHPX';
	}

	public function isRegistered()
	{
		return defined('MESSAGESERIVICE_ALLOW_IMHPX') && MESSAGESERIVICE_ALLOW_IMHPX === true;
	}

	public function canUse()
	{
		return $this->isRegistered();
	}

	public function getFromList()
	{
		$id = $this->getOption(self::SUBJECT_OPTION);
		return [
			[
				'id' => $id,
				'name' => $id,
			],
		];
	}


	public function register(array $fields): Result
	{
		$this->setOption(static::ENDPOINT_OPTION, $fields['connector_endpoint']);
		$this->setOption(static::LOGIN_OPTION, $fields['login']);
		$this->setOption(static::PASSWORD_OPTION, $fields['password']);
		$this->setOption(static::SUBJECT_OPTION, $fields['subject_id']);

		return new Result();
	}

	public function getOwnerInfo()
	{
		return [
			self::ENDPOINT_OPTION => $this->getOption(self::ENDPOINT_OPTION),
			self::LOGIN_OPTION => $this->getOption(self::LOGIN_OPTION),
			self::PASSWORD_OPTION => $this->getOption(self::PASSWORD_OPTION),
			self::SUBJECT_OPTION => $this->getOption(self::SUBJECT_OPTION),
		];
	}

	public function getCallbackUrl(): string
	{
		return parent::getCallbackUrl();
	}

	public function getExternalManageUrl()
	{
		// TODO: Implement getExternalManageUrl() method.
	}

	public function sendMessage(array $messageFields): Sender\Result\SendMessage
	{
		$result = new Sender\Result\SendMessage();
		if (!$this->canUse())
		{
			return $result->addError(new Error('Service is unavailable'));
		}

		$body = $this->makeBodyOutgoingMessage($messageFields);

		$requestResult = $this->callExternalMethod($body);
		if (!$requestResult->isSuccess())
		{
			$result->addErrors($requestResult->getErrors());

			return $result;
		}

		$response = $requestResult->getHttpResponse();
		$this->processServiceResponse($response, $result);

		return $result;
	}

	public function getMessageStatus(array $messageFields)
	{
		// TODO: Implement getMessageStatus() method.
	}

	public static function resolveStatus($serviceStatus): ?int
	{
		switch ($serviceStatus)
		{
			case 'read':
			case 'sent':
				return MessageStatus::SENT;
			case 'enqueued':
				return MessageStatus::QUEUED;
			case 'delayed':
				return MessageStatus::ACCEPTED;
			case 'delivered':
				return MessageStatus::DELIVERED;
			case 'undelivered':
				return MessageStatus::UNDELIVERED;
			case 'failed':
			case 'cancelled':
			case 'expired':
			case 'no-match-template':
				return MessageStatus::FAILED;
			default:
				return mb_strpos($serviceStatus, 'error') === 0 ? MessageStatus::ERROR : MessageStatus::UNKNOWN;
		}
	}

	protected function makeBodyOutgoingMessage(array $messageFields): string
	{
		$messageText = $messageFields['MESSAGE_BODY'];
		$messageText = htmlspecialcharsbx($messageText);

		$smsBlock = "<sms>
				<subject>Bitrix24</subject>
				<priority>{$this->getPriority()}</priority>
				<content>{$messageText}</content>
				<sendTimeoutSeconds>60</sendTimeoutSeconds>
				<validityPeriodMinutes>30</validityPeriodMinutes>
			</sms>";

		$address = static::normalizePhoneNumberForOutgoing($messageFields['MESSAGE_TO']);

		$ownerInfo = $this->getOwnerInfo();
		$login = $ownerInfo[static::LOGIN_OPTION];
		$password = $ownerInfo[static::PASSWORD_OPTION];
		$mailbox = $ownerInfo[static::SUBJECT_OPTION];

		$template = <<<XML
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<consumeInstantMessageRequest>
	<header>
		<auth>
			<login>{$login}</login>
        	<password>{$password}</password>
		</auth>
	</header>
	<payload>
		<instantMessageList>
			<instantMessage clientId="{$messageFields['ID']}">
				<address>{$address}</address>
				<subject>{$mailbox}</subject>
				<priority>{$this->getPriority()}</priority>
				<instantMessageType>{$this->getMessageType()}</instantMessageType>
				<contentType>text</contentType>
				<content>
					<text>{$messageText}</text>
				</content>
				$smsBlock
			</instantMessage>
		</instantMessageList>
	</payload>
</consumeInstantMessageRequest>
XML;

		return $template;
	}

	protected function callExternalMethod(string $body): Sender\Result\HttpRequestResult
	{
		$httpClient = new HttpClient([
			"socketTimeout" => $this->socketTimeout,
			"streamTimeout" => $this->streamTimeout,
			'waitResponse' => true,
		]);
		$httpClient->setHeader('User-Agent', 'Bitrix24');
		$httpClient->setHeader('Content-type', 'text/xml');

		$result = new Sender\Result\HttpRequestResult();
		$result->setHttpRequest(new DTO\Request([
			'method' => HttpClient::HTTP_POST,
			'uri' => $this->getServiceEndpoint(),
			'headers' => method_exists($httpClient, 'getRequestHeaders') ? $httpClient->getRequestHeaders()->toArray() : [],
			'body' => $body
		]));

		if (!$httpClient->query(HttpClient::HTTP_POST, $this->getServiceEndpoint(), $body))
		{
			$result->setHttpResponse(new DTO\Response([
				'error' => Sender\Util::getHttpClientErrorString($httpClient)
			]));
			$httpError = $httpClient->getError();
			$errorCode = array_key_first($httpError);
			$result->addError(new Error($httpError[$errorCode], $errorCode));
			return $result;
		}

		$httpResponse = new DTO\Response([
			'statusCode' => $httpClient->getStatus(),
			'headers' => $httpClient->getHeaders()->toArray(),
			'body' => $httpClient->getResult(),
		]);
		$result->setHttpResponse($httpResponse);

		return $result;
	}

	/**
	 *
	 * Response body example:
	 * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
	 * <consumeInstantMessageResponse>
	 *   <header/>
	 *   <payload>
	 *     <code>ok</code>
	 *     <instantMessageList>
	 *       <instantMessage clientId="9991144" providerId="87952036">
	 *         <code>ok</code>
	 *       </instantMessage>
	 *     </instantMessageList>
	 *   </payload>
	 * </consumeInstantMessageResponse>
	 *
	 * Where:
	 *  - clientId: our message id
	 *  - providerId: their message id
	 *  - code: status code ("ok" or one of the error codes, like "error-syntax", "error-auth", "error-permission", etc.)
	 *
	 * @param DTO\Response $response Response for our send request.
	 * @param Sender\Result\SendMessage $result Request to be filled.
	 */
	public function processServiceResponse(DTO\Response $response, Sender\Result\SendMessage $result): void
	{
		if ($response->statusCode !== 200)
		{
			$result->addError(new Error("Response status code is {$response->statusCode}", 'WRONG_SERVICE_RESPONSE_CODE'));
			return;
		}
		$parseResult = $this->parseXml($response->body);
		if (!$parseResult->isSuccess())
		{
			$result->addError(
				new Error(
					'XML parse error: ' . implode('; ', $parseResult->getErrorMessages()),
					'XML_PARSE_ERROR'
				)
			);
			return;
		}

		/** @var \SimpleXMLElement $instantMessageResponse */
		$instantMessageResponse = $parseResult->getData()['root'];

		// hack to convert SimpleXMLElement to array
		$instantMessageResponse = Json::decode(Json::encode($instantMessageResponse));

		// response structure
		if (
			!isset($instantMessageResponse['payload'])
			|| (!isset($instantMessageResponse['payload']['code'])
				&& !isset($instantMessageResponse['payload']['instantMessageList'])
			)
		)
		{
			$result->addError(new Error('Wrong xml response structure', 'SERVICE_RESPONSE_PARSE_ERROR'));
			return;
		}

		if ($instantMessageResponse['payload']['code'] !== 'ok')
		{
			$result->setStatus(\Bitrix\MessageService\MessageStatus::ERROR);
			$result->addError(new Error($instantMessageResponse['payload']['code'], $instantMessageResponse['payload']['code']));
			return;
		}

		foreach ($instantMessageResponse['payload']['instantMessageList'] as $instantMessage)
		{
			if ($instantMessage['code'] === 'ok' && isset($instantMessage['@attributes']['providerId']))
			{
				$result->setExternalId($instantMessage['@attributes']['providerId']);
				$result->setAccepted();
			}
			else
			{
				$result->setStatus(\Bitrix\MessageService\MessageStatus::ERROR);
				$result->addError(new Error('', $instantMessage['code']));
			}
			// we expect only one message response here
			return;
		}

		$result->addError(new Error('Could not find message status in response', 'SERVICE_RESPONSE_PARSE_ERROR'));
	}

	/**
	 * @param string $incomingRequestBody (see example)
	 * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
	 *	<provideInstantMessageDlvStatusResponse>
	 *		<header/>
	 *		<payload>
	 *			<code>ok</code>
	 *			<instantMessageList>
	 *				<instantMessage providerId="6042">
	 *					<code>ok</code>
	 *					<instantMessageDlvStatus>
	 *						<dlvStatus>read</dlvStatus>
	 *						<dlvStatusAt>2021-03-24 10:53:55</dlvStatusAt>
	 *					</instantMessageDlvStatus>
	 *				</instantMessage>
	 *			</instantMessageList>
	 *		</payload>
	 *	</provideInstantMessageDlvStatusResponse>
	 *
	 * @return DTO\Response
	 */
	public function processIncomingRequest(string $incomingRequestBody): DTO\Response
	{
		$response = new DTO\Response([
			'statusCode' => 200
		]);
		$parseResult = $this->parseIncomingRequest($incomingRequestBody);
		if (!$parseResult->isSuccess())
		{
			$response->statusCode = 400;
			$response->body = 'Parse error';

			return $response;
		}

		/** @var DTO\StatusUpdate[] $statusUpdateList */
		$statusUpdateList = $parseResult->getData();
		foreach ($statusUpdateList as $statusUpdate)
		{
			$message = Message::loadByExternalId(static::ID, $statusUpdate->externalId);
			if ($message && $statusUpdate->providerStatus != '')
			{
				$message->updateStatusByExternalStatus($statusUpdate->providerStatus);
			}
		}

		return $response;
	}

	/**
	 * @param string $incomingRequestBody (see example)
	 * <?xml version="1.0" encoding="UTF-8" standalone="no"?>
	 *	<provideInstantMessageDlvStatusResponse>
	 *		<header/>
	 *		<payload>
	 *			<code>ok</code>
	 *			<instantMessageList>
	 *				<instantMessage providerId="6042">
	 *					<code>ok</code>
	 *					<instantMessageDlvStatus>
	 *						<dlvStatus>read</dlvStatus>
	 *						<dlvStatusAt>2021-03-24 10:53:55</dlvStatusAt>
	 *					</instantMessageDlvStatus>
	 *				</instantMessage>
	 *			</instantMessageList>
	 *		</payload>
	 *	</provideInstantMessageDlvStatusResponse>
	 *
	 * @return Result Result with []DTO\StatusUpdate
	 */
	public function parseIncomingRequest(string $incomingRequestBody): Result
	{
		$result = new Result();
		$parseResult = $this->parseXml($incomingRequestBody);
		if (!$parseResult->isSuccess())
		{
			return $result->addErrors($parseResult->getErrors());
		}

		/** @var \SimpleXMLElement $incomingRequest */
		$incomingRequest = $parseResult->getData()['root'];

		// incoming messages are not supported yet
		if ($incomingRequest->getName() != 'provideInstantMessageDlvStatusResponse')
		{
			return $result;
		}

		// hack to convert SimpleXMLElement to array
		$incomingRequest = Json::decode(Json::encode($incomingRequest));
		if (
			!isset($incomingRequest['payload'])
			|| (!isset($incomingRequest['payload']['code'])
				&& !isset($incomingRequest['payload']['instantMessageList'])
			)
		)
		{
			return $result->addError(new Error('Wrong XML structure'));
		}

		$statusUpdateList = [];

		// If response contains only one message delivery report - <instantMessage> element will contain the report
		// If response contains more than on message delivery report - <instantMessage> element will contain array of reports
		$instantMessageList = $incomingRequest['payload']['instantMessageList']['instantMessage'];
		if (!is_array($instantMessageList))
		{
			//empty list
			return $result->setData($statusUpdateList);
		}

		if (Collection::isAssociative($instantMessageList))
		{
			$instantMessageList = [$instantMessageList];
		}

		foreach ($instantMessageList as $instantMessage)
		{
			$statusUpdateList[] = new DTO\StatusUpdate([
				'externalId' => (int)$instantMessage['@attributes']['providerId'],
				'providerStatus' => $instantMessage['instantMessageDlvStatus']['dlvStatus'],
				'deliveryStatus' => static::resolveStatus($instantMessage['instantMessageDlvStatus']['dlvStatus']),
				'deliveryError' => $instantMessage['instantMessageDlvStatus']['dlvError']
			]);
		}

		return $result->setData($statusUpdateList);
	}

	/**
	 * @param string $xmlString XML document.
	 * @return Result Returns Result with the following keys (in the case of success)
	 *  - root {SimpleXMLElement} Root element of the XML document.
	 */
	protected function parseXml(string $xmlString): Result
	{
		$result = new Result();

		if ($xmlString === '')
		{
			return $result->addError(new Error('Empty XML'));
		}

		libxml_use_internal_errors(true);
		libxml_clear_errors();
		$parsedBody = simplexml_load_string($xmlString);
		$parseErrors = libxml_get_errors();

		if (!empty($parseErrors))
		{
			/** @var \LibXMLError $parseError */
			foreach ($parseErrors as $parseError)
			{
				$result->addError(new Error($parseError->message, $parseError->code));
			}
			return $result;
		}

		$result->setData([
			'root' => $parsedBody
		]);
		return $result;
	}

	protected function getPriority()
	{
		return $this::DEFAULT_PRIORITY;
	}

	protected function getMessageType()
	{
		return $this::MESSAGE_TYPE;
	}

	public function getServiceEndpoint(): string
	{
		return $this->getOption(static::ENDPOINT_OPTION);
	}

	public static function normalizePhoneNumberForOutgoing(string $phoneNumber): string
	{
		// remove +
		if (mb_strpos($phoneNumber, '+') === 0)
		{
			return mb_substr($phoneNumber, 1);
		}

		return $phoneNumber;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit