mirror of https://github.com/movim/movim
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
439 lines
14 KiB
439 lines
14 KiB
<?php
|
|
|
|
namespace Moxl\Stanza;
|
|
|
|
use App\MessageFile;
|
|
use App\MessageOmemoHeader;
|
|
use Movim\Session;
|
|
|
|
class Message
|
|
{
|
|
public static function maker(
|
|
string $to,
|
|
?string $content = null,
|
|
?string $html = null,
|
|
?string $type = null,
|
|
?string $chatstates = null,
|
|
?string $receipts = null,
|
|
?string $id = null,
|
|
?string $replace = null,
|
|
?MessageFile $file = null,
|
|
$invite = false,
|
|
$parentId = false,
|
|
array $reactions = [],
|
|
$originId = false,
|
|
$threadId = false,
|
|
$replyId = false,
|
|
$replyTo = false,
|
|
$replyQuotedBodyLength = 0,
|
|
?MessageOmemoHeader $messageOMEMO = null
|
|
) {
|
|
$session = Session::instance();
|
|
|
|
$dom = new \DOMDocument('1.0', 'UTF-8');
|
|
$root = $dom->createElementNS('jabber:client', 'message');
|
|
$dom->appendChild($root);
|
|
$root->setAttribute('to', $to);
|
|
|
|
if ($type != null) {
|
|
$root->setAttribute('type', $type);
|
|
}
|
|
|
|
if ($receipts != null && in_array($receipts, ['received', 'displayed'])) {
|
|
$root->setAttribute('id', generateUUID());
|
|
} elseif ($id != null) {
|
|
$root->setAttribute('id', $id);
|
|
} else {
|
|
$root->setAttribute('id', $session->get('id'));
|
|
}
|
|
|
|
/**
|
|
* https://xmpp.org/extensions/xep-0045.html#privatemessage
|
|
* Resource on the to, we assume that it's a MUC PM
|
|
*/
|
|
if (explodeJid($to)['resource'] !== null) {
|
|
$xuser = $dom->createElementNS('http://jabber.org/protocol/muc#user', 'x');
|
|
$root->appendChild($xuser);
|
|
}
|
|
|
|
// Thread
|
|
if ($threadId) {
|
|
$thread = $dom->createElement('thread', $threadId);
|
|
$root->appendChild($thread);
|
|
}
|
|
|
|
// Message replies
|
|
if ($replyId != false && $replyTo != false) {
|
|
$reply = $dom->createElementNS('urn:xmpp:reply:0', 'reply');
|
|
$reply->setAttribute('id', $replyId);
|
|
$reply->setAttribute('to', $replyTo);
|
|
$root->appendChild($reply);
|
|
|
|
if ($replyQuotedBodyLength > 0) {
|
|
$fallback = $dom->createElementNS('urn:xmpp:fallback:0', 'fallback');
|
|
$fallback->setAttribute('for', 'urn:xmpp:reply:0');
|
|
|
|
$fallbackBody = $dom->createElement('body');
|
|
$fallbackBody->setAttribute('start', 0);
|
|
$fallbackBody->setAttribute('end', $replyQuotedBodyLength);
|
|
|
|
$fallback->appendChild($fallbackBody);
|
|
|
|
$root->appendChild($fallback);
|
|
}
|
|
}
|
|
|
|
// Chatstates
|
|
if ($chatstates != null && $content == null) {
|
|
$chatstate = $dom->createElementNS('http://jabber.org/protocol/chatstates', $chatstates);
|
|
$root->appendChild($chatstate);
|
|
}
|
|
|
|
if ($content != null) {
|
|
$chatstate = $dom->createElementNS('http://jabber.org/protocol/chatstates', 'active');
|
|
$root->appendChild($chatstate);
|
|
|
|
$body = $dom->createElement('body');
|
|
|
|
|
|
$bodyContent = (preg_match('(>|<|&)', $content) === 1)
|
|
? $dom->createCDATASection($content)
|
|
: $dom->createTextNode($content);
|
|
$body->appendChild($bodyContent);
|
|
$root->appendChild($body);
|
|
}
|
|
|
|
if ($replace != null) {
|
|
$rep = $dom->createElementNS('urn:xmpp:message-correct:0', 'replace');
|
|
$rep->setAttribute('id', $replace);
|
|
$root->appendChild($rep);
|
|
}
|
|
|
|
if ($html != null) {
|
|
$xhtml = $dom->createElement('html');
|
|
$xhtml->setAttribute('xmlns', 'http://jabber.org/protocol/xhtml-im');
|
|
$body = $dom->createElement('body');
|
|
$body->setAttribute('xmlns', 'http://www.w3.org/1999/xhtml');
|
|
|
|
$dom2 = new \DOMDocument('1.0', 'UTF-8');
|
|
$dom2->loadXml('<root>' . $html . '</root>');
|
|
$bar = $dom2->documentElement->firstChild; // we want to import the bare tree
|
|
$body->appendChild($dom->importNode($bar, true));
|
|
|
|
$xhtml->appendChild($body);
|
|
$root->appendChild($xhtml);
|
|
}
|
|
|
|
if ($receipts != null) {
|
|
if ($receipts == 'request') {
|
|
$request = $dom->createElementNS('urn:xmpp:receipts', 'request');
|
|
} elseif ($receipts == 'received') {
|
|
$request = $dom->createElement('received');
|
|
$request->setAttribute('id', $id);
|
|
$request->setAttribute('xmlns', 'urn:xmpp:receipts');
|
|
$root->appendChild($request);
|
|
} elseif ($receipts == 'displayed') {
|
|
$request = $dom->createElement('displayed');
|
|
$request->setAttribute('id', $id);
|
|
$request->setAttribute('xmlns', 'urn:xmpp:chat-markers:0');
|
|
}
|
|
|
|
$root->appendChild($request);
|
|
|
|
if ($receipts == 'received') {
|
|
$nostore = $dom->createElementNS('urn:xmpp:hints', 'no-store');
|
|
$root->appendChild($nostore);
|
|
|
|
$nocopy = $dom->createElementNS('urn:xmpp:hints', 'no-copy');
|
|
$root->appendChild($nocopy);
|
|
}
|
|
}
|
|
|
|
if (
|
|
!in_array($receipts, ['received', 'displayed'])
|
|
&& $chatstates == 'active'
|
|
) {
|
|
$markable = $dom->createElementNS('urn:xmpp:chat-markers:0', 'markable');
|
|
$root->appendChild($markable);
|
|
}
|
|
|
|
if ($file != null) {
|
|
$reference = $dom->createElement('reference');
|
|
$reference->setAttribute('xmlns', 'urn:xmpp:reference:0');
|
|
$reference->setAttribute('type', 'data');
|
|
|
|
// SIMS
|
|
if (isset($file->type) && isset($file->name) && isset($file->size)) {
|
|
$media = $dom->createElement('media-sharing');
|
|
$media->setAttribute('xmlns', 'urn:xmpp:sims:1');
|
|
$reference->appendChild($media);
|
|
|
|
$filen = $dom->createElement('file');
|
|
$filen->setAttribute('xmlns', 'urn:xmpp:jingle:apps:file-transfer:4');
|
|
$media->appendChild($filen);
|
|
|
|
// xmpp/uri is an internal placeholder
|
|
if ($file->type != 'xmpp/uri') {
|
|
$filen->appendChild($dom->createElement('media-type', $file->type));
|
|
}
|
|
|
|
if (!empty((string)$file->name)) {
|
|
$filen->appendChild($dom->createElement('name', $file->name));
|
|
}
|
|
|
|
if ((int)$file->size > 0) {
|
|
$filen->appendChild($dom->createElement('size', $file->size));
|
|
}
|
|
|
|
$sources = $dom->createElement('sources');
|
|
$media->appendChild($sources);
|
|
|
|
$sreference = $dom->createElement('reference');
|
|
$sreference->setAttribute('xmlns', 'urn:xmpp:reference:0');
|
|
$sreference->setAttribute('type', 'data');
|
|
$sreference->setAttribute('uri', $file->url);
|
|
|
|
if (isset($file->thumbnail_url)) {
|
|
$thumbnail = $dom->createElement('thumbnail');
|
|
$thumbnail->setAttribute('xmlns', 'urn:xmpp:thumbs:1');
|
|
$thumbnail->setAttribute('media-type', $file->thumbnail_type);
|
|
|
|
if ($file->thumbnail_type == 'image/thumbhash') {
|
|
$thumbnail->setAttribute('uri', 'data:image/thumbhash,' . $file->thumbnail_url);
|
|
} else {
|
|
$thumbnail->setAttribute('uri', $file->thumbnail_url);
|
|
}
|
|
|
|
$thumbnail->setAttribute('width', $file->thumbnail_width);
|
|
$thumbnail->setAttribute('height', $file->thumbnail_height);
|
|
|
|
$filen->appendChild($thumbnail);
|
|
}
|
|
|
|
$sources->appendChild($sreference);
|
|
} else {
|
|
$reference->setAttribute('uri', $file->url);
|
|
}
|
|
|
|
$root->appendChild($reference);
|
|
|
|
// OOB
|
|
$x = $dom->createElement('x');
|
|
$x->setAttribute('xmlns', 'jabber:x:oob');
|
|
$x->appendChild($dom->createElement('url', $file->url));
|
|
|
|
$root->appendChild($x);
|
|
}
|
|
|
|
if ($invite != false) {
|
|
$x = $dom->createElement('x');
|
|
$x->setAttribute('xmlns', 'http://jabber.org/protocol/muc#user');
|
|
$root->appendChild($x);
|
|
|
|
$xinvite = $dom->createElement('invite');
|
|
$xinvite->setAttribute('to', $invite);
|
|
$x->appendChild($xinvite);
|
|
}
|
|
|
|
if ($parentId != false) {
|
|
$reactionsn = $dom->createElement('reactions');
|
|
$reactionsn->setAttribute('xmlns', 'urn:xmpp:reactions:0');
|
|
$reactionsn->setAttribute('id', $parentId);
|
|
|
|
foreach ($reactions as $emoji) {
|
|
$reaction = $dom->createElement('reaction', $emoji);
|
|
$reactionsn->appendChild($reaction);
|
|
}
|
|
$root->appendChild($reactionsn);
|
|
|
|
// Force the storage of the reactions in the archive
|
|
$store = $dom->createElement('store');
|
|
$store->setAttribute('xmlns', 'urn:xmpp:hints');
|
|
$root->appendChild($store);
|
|
}
|
|
|
|
if ($originId != false) {
|
|
$origin = $dom->createElement('origin-id');
|
|
$origin->setAttribute('xmlns', 'urn:xmpp:sid:0');
|
|
$origin->setAttribute('id', $originId);
|
|
$root->appendChild($origin);
|
|
}
|
|
|
|
// OMEMO
|
|
if ($messageOMEMO) {
|
|
$encryption = $dom->createElement('encryption');
|
|
$encryption->setAttribute('xmlns', 'urn:xmpp:eme:0');
|
|
$encryption->setAttribute('name', 'OMEMOE');
|
|
$encryption->setAttribute('namespace', 'eu.siacs.conversations.axolotl');
|
|
$root->appendChild($encryption);
|
|
|
|
$messageOMEMOXML = $dom->importNode($messageOMEMO->getDom(), true);
|
|
$root->appendChild($messageOMEMOXML);
|
|
}
|
|
|
|
\Moxl\API::request($dom->saveXML($dom->documentElement));
|
|
}
|
|
|
|
public static function message(
|
|
string $to,
|
|
?string $content = null,
|
|
?string $html = null,
|
|
?string $id = null,
|
|
?string $replace = null,
|
|
?MessageFile $file = null,
|
|
$parentId = false,
|
|
array $reactions = [],
|
|
$originId = false,
|
|
$threadId = false,
|
|
$replyId = false,
|
|
$replyTo = false,
|
|
$replyQuotedBodyLength = 0,
|
|
?MessageOmemoHeader $messageOMEMO = null
|
|
) {
|
|
self::maker(
|
|
$to,
|
|
$content,
|
|
$html,
|
|
'chat',
|
|
'active',
|
|
'request',
|
|
$id,
|
|
$replace,
|
|
$file,
|
|
false,
|
|
$parentId,
|
|
$reactions,
|
|
$originId,
|
|
$threadId,
|
|
$replyId,
|
|
$replyTo,
|
|
$replyQuotedBodyLength,
|
|
$messageOMEMO
|
|
);
|
|
}
|
|
|
|
public static function simpleMessage(
|
|
string $to,
|
|
?string $content = null,
|
|
?string $html = null,
|
|
string $id = null,
|
|
string $replace = null,
|
|
?MessageFile $file = null,
|
|
$parentId = false,
|
|
array $reactions = [],
|
|
$originId = false,
|
|
$threadId = false,
|
|
$replyId = false,
|
|
$replyTo = false,
|
|
$replyQuotedBodyLength = 0,
|
|
?MessageOmemoHeader $messageOMEMO = null
|
|
) {
|
|
self::maker(
|
|
$to,
|
|
$content,
|
|
$html,
|
|
'chat',
|
|
false,
|
|
false,
|
|
$id,
|
|
$replace,
|
|
$file,
|
|
false,
|
|
$parentId,
|
|
$reactions,
|
|
$originId,
|
|
$threadId,
|
|
$replyId,
|
|
$replyTo,
|
|
$replyQuotedBodyLength,
|
|
$messageOMEMO
|
|
);
|
|
}
|
|
|
|
public static function received($to, $id, $type = 'chat')
|
|
{
|
|
self::maker($to, type: $type, receipts: 'received', id: $id);
|
|
}
|
|
|
|
public static function displayed($to, $id, $type = 'chat')
|
|
{
|
|
self::maker($to, type: $type, receipts: 'displayed', id: $id);
|
|
}
|
|
|
|
public static function invite($to, $id, $invite)
|
|
{
|
|
self::maker($to, id: $id, invite: $invite);
|
|
}
|
|
|
|
public static function active($to)
|
|
{
|
|
self::maker($to, type: 'chat', chatstates: 'active');
|
|
}
|
|
|
|
public static function inactive($to)
|
|
{
|
|
self::maker($to, type: 'chat', chatstates: 'inactive');
|
|
}
|
|
|
|
public static function composing($to)
|
|
{
|
|
self::maker($to, type: 'chat', chatstates: 'composing');
|
|
}
|
|
|
|
public static function paused($to)
|
|
{
|
|
self::maker($to, type: 'chat', chatstates: 'paused');
|
|
}
|
|
|
|
public static function retract(string $to, string $originId)
|
|
{
|
|
$dom = new \DOMDocument('1.0', 'UTF-8');
|
|
$root = $dom->createElementNS('jabber:client', 'message');
|
|
$dom->appendChild($root);
|
|
$root->setAttribute('to', str_replace(' ', '\40', $to));
|
|
$root->setAttribute('type', 'chat');
|
|
$root->setAttribute('id', generateUUID());
|
|
|
|
$retract = $dom->createElement('retract');
|
|
$retract->setAttribute('xmlns', 'urn:xmpp:message-retract:1');
|
|
$retract->setAttribute('id', $originId);
|
|
$root->appendChild($retract);
|
|
|
|
// Hints
|
|
$store = $dom->createElement('store');
|
|
$store->setAttribute('xmlns', 'urn:xmpp:hints');
|
|
$root->appendChild($store);
|
|
|
|
// Fallback
|
|
$fallback = $dom->createElementNS('urn:xmpp:fallback:0', 'fallback');
|
|
$fallback->setAttribute('for', 'urn:xmpp:message-retract:1');
|
|
$root->appendChild($fallback);
|
|
|
|
$body = $dom->createElement('body');
|
|
$bodyContent = $dom->createTextNode(__('message.retract_body'));
|
|
$body->appendChild($bodyContent);
|
|
$root->appendChild($body);
|
|
|
|
\Moxl\API::request($dom->saveXML($dom->documentElement));
|
|
}
|
|
|
|
public static function moderate(string $to, string $stanzaId)
|
|
{
|
|
$dom = new \DOMDocument('1.0', 'UTF-8');
|
|
|
|
$moderate = $dom->createElement('moderate');
|
|
$moderate->setAttribute('xmlns', 'urn:xmpp:message-moderate:1');
|
|
$moderate->setAttribute('id', $stanzaId);
|
|
|
|
$reason = $dom->createElement('reason');
|
|
$reasonContent = $dom->createTextNode(__('message.moderate_body'));
|
|
$reason->appendChild($reasonContent);
|
|
$moderate->appendChild($reason);
|
|
|
|
$retract = $dom->createElement('retract');
|
|
$retract->setAttribute('xmlns', 'urn:xmpp:message-retract:1');
|
|
$moderate->appendChild($retract);
|
|
|
|
\Moxl\API::request(\Moxl\API::iqWrapper($moderate, $to, 'set'));
|
|
}
|
|
}
|