diff --git a/appinfo/application.php b/appinfo/application.php index 636ce71b..be738441 100644 --- a/appinfo/application.php +++ b/appinfo/application.php @@ -247,7 +247,9 @@ public function __construct(array $urlParams=array()){ return new ContactsStoreUserProvider( $c->query('OCP\Contacts\ContactsMenu\IContactsStore'), $c->query('ServerContainer')->getUserSession(), - $c->query('ServerContainer')->getUserManager() + $c->query('ServerContainer')->getUserManager(), + $c->query('OCP\IGroupManager'), + $c->query('OCP\IConfig') ); } else { return new UserManagerUserProvider( diff --git a/js/jsxc b/js/jsxc index 4c2e01bf..c804418a 160000 --- a/js/jsxc +++ b/js/jsxc @@ -1 +1 @@ -Subproject commit 4c2e01bfb6396b73a0e4abcfe4b91110af7cb918 +Subproject commit c804418ad58358469fe192674cad74de6247077d diff --git a/js/ojsxc.js b/js/ojsxc.js index dbc37c59..ed5ca3be 100644 --- a/js/ojsxc.js +++ b/js/ojsxc.js @@ -325,6 +325,16 @@ jsxc.storage.removeUserItem('defaultAvatars'); }); + $(document).on('connfail.jsxc', function(ev, condition) { + if (condition === 'x-nc-not_allowed_to_chat') { + jsxc.gui.roster.toggle(jsxc.CONST.HIDDEN); + $('.jsxc_chatIcon').remove(); + jsxc.storage.removeItem('jid'); + jsxc.storage.removeItem('sid'); + jsxc.storage.removeItem('rid'); + } + }); + $(document).on('status.contacts.count status.contact.updated', function() { if (jsxc.restoreCompleted) { setTimeout(function() { @@ -388,5 +398,7 @@ if ($('#contactsmenu').length > 0) { observeContactsMenu(); } + }); + }(jQuery)); diff --git a/lib/ContactsStoreUserProvider.php b/lib/ContactsStoreUserProvider.php index 6f06d4ce..feac35fb 100644 --- a/lib/ContactsStoreUserProvider.php +++ b/lib/ContactsStoreUserProvider.php @@ -2,6 +2,9 @@ namespace OCA\OJSXC; +use OCP\IConfig; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IUserManager; use OCP\IUserSession; @@ -28,11 +31,23 @@ class ContactsStoreUserProvider implements IUserProvider */ private $userManager; - public function __construct($contactsStore, IUserSession $userSession, IUserManager $userManager) + /** + * @var IGroupManager + */ + private $groupManager; + + /** + * @var IConfig + */ + private $config; + + public function __construct($contactsStore, IUserSession $userSession, IUserManager $userManager, IGroupManager $groupManager, IConfig $config) { $this->contactsStore = $contactsStore; $this->userSession = $userSession; $this->userManager = $userManager; + $this->groupManager = $groupManager; + $this->config = $config; } public function getAllUsers() @@ -87,4 +102,21 @@ public function hasUserForUserByUID($uid1, $uid2) { return !is_null($this->contactsStore->findOne($this->userManager->get($uid1), 0, $uid2)); } + + public function isUserExcluded($userId) + { + if ($this->config->getAppValue('core', 'shareapi_exclude_groups', 'no') === 'yes') { + $user = $this->userManager->get($userId); + $user_groups = $this->groupManager->getUserGroupIds($user); + $excludedGroups = $this->config->getAppValue('core', 'shareapi_exclude_groups_list', ''); + $decodedExcludeGroups = json_decode($excludedGroups, true); + $excludeGroupsList = ($decodedExcludeGroups !== null) ? $decodedExcludeGroups : []; + + if (count(array_intersect($excludeGroupsList, $user_groups)) !== 0) { + // a group of the current user is excluded -> filter all local users + return true; + } + } + return false; + } } diff --git a/lib/Controller/HttpBindController.php b/lib/Controller/HttpBindController.php index f5206e30..a7e9e063 100644 --- a/lib/Controller/HttpBindController.php +++ b/lib/Controller/HttpBindController.php @@ -5,6 +5,7 @@ use OCA\OJSXC\Db\Presence; use OCA\OJSXC\Db\PresenceMapper; use OCA\OJSXC\Db\StanzaMapper; +use OCA\OJSXC\Exceptions\TerminateException; use OCA\OJSXC\Http\XMPPResponse; use OCA\OJSXC\ILock; use OCA\OJSXC\NewContentContainer; @@ -162,51 +163,56 @@ public function index() $input = $this->body; $longpoll = true; // set to false when the response should directly be returned and no polling should be done $longpollStart = true; // start the first long poll cycle - if (!empty($input)) { - // replace invalid XML by valid XML one - $input = str_replace("", "", $input); - $reader = new Reader(); - $reader->xml($input); - $reader->elementMap = [ - '{jabber:client}message' => 'Sabre\Xml\Element\KeyValue', - '{jabber:client}presence' => function (Reader $reader) { - return Presence::createFromXml($reader, $this->userId); + try { + if (!empty($input)) { + // replace invalid XML by valid XML one + $input = str_replace("", "", $input); + $reader = new Reader(); + $reader->xml($input); + $reader->elementMap = [ + '{jabber:client}message' => 'Sabre\Xml\Element\KeyValue', + '{jabber:client}presence' => function (Reader $reader) { + return Presence::createFromXml($reader, $this->userId); + } + ]; + $stanzas = null; + try { + $stanzas = $reader->parse(); + } catch (LibXMLException $e) { } - ]; - $stanzas = null; - try { - $stanzas = $reader->parse(); - } catch (LibXMLException $e) { - } - if (!is_null($stanzas) && count($stanzas['value']) > 0) { - $this->stanzaLogger->logRaw($input, StanzaLogger::RECEIVING); - } - if (!is_null($stanzas)) { - $stanzas = $stanzas['value']; - if (is_array($stanzas)) { - foreach ($stanzas as $stanza) { - $stanzaType = $this->getStanzaType($stanza); - if ($stanzaType === self::MESSAGE) { - $this->messageHandler->handle($stanza); - } elseif ($stanzaType === self::IQ) { - $result = $this->iqHandler->handle($stanza); - if (!is_null($result)) { - $longpoll = false; - $this->response->write($result); - } - } elseif ($stanza['value'] instanceof Presence) { - $results = $this->presenceHandler->handle($stanza['value']); - if (!is_null($results) && is_array($results)) { - $longpoll = false; - $longpollStart = false; - foreach ($results as $r) { - $this->response->write($r); + if (!is_null($stanzas) && count($stanzas['value']) > 0) { + $this->stanzaLogger->logRaw($input, StanzaLogger::RECEIVING); + } + if (!is_null($stanzas)) { + $stanzas = $stanzas['value']; + if (is_array($stanzas)) { + foreach ($stanzas as $stanza) { + $stanzaType = $this->getStanzaType($stanza); + if ($stanzaType === self::MESSAGE) { + $this->messageHandler->handle($stanza); + } elseif ($stanzaType === self::IQ) { + $result = $this->iqHandler->handle($stanza); + if (!is_null($result)) { + $longpoll = false; + $this->response->write($result); + } + } elseif ($stanza['value'] instanceof Presence) { + $results = $this->presenceHandler->handle($stanza['value']); + if (!is_null($results) && is_array($results)) { + $longpoll = false; + $longpollStart = false; + foreach ($results as $r) { + $this->response->write($r); + } } } } } } } + } catch (TerminateException $e) { + $this->response->terminate(); + return $this->response; } // Start long polling diff --git a/lib/Exceptions/TerminateException.php b/lib/Exceptions/TerminateException.php new file mode 100644 index 00000000..1b7fdb4c --- /dev/null +++ b/lib/Exceptions/TerminateException.php @@ -0,0 +1,8 @@ +hasUserByUID($uid2); } + + public function isUserExcluded($userId) + { + // to limit inconsistency we only support the settings in NC > 13.0.0 + return false; + } } diff --git a/lib/http/xmppresponse.php b/lib/http/xmppresponse.php index 349016f0..8680bd7b 100644 --- a/lib/http/xmppresponse.php +++ b/lib/http/xmppresponse.php @@ -60,4 +60,17 @@ public function render() $this->writer->endElement(); return $this->writer->outputMemory(); } + + /** + * Terminates the Chat connection with the `x-nc-not_allowed_to_chat` condition. + */ + public function terminate() + { + $this->writer = new Writer(); + $this->writer->openMemory(); + $this->writer->startElement('body'); + $this->writer->writeAttribute('xmlns', 'http://jabber.org/protocol/httpbind'); + $this->writer->writeAttribute('type', 'terminate'); + $this->writer->writeAttribute('condition', 'x-nc-not_allowed_to_chat'); + } } diff --git a/lib/stanzahandlers/iq.php b/lib/stanzahandlers/iq.php index f2d33de2..70f5428e 100644 --- a/lib/stanzahandlers/iq.php +++ b/lib/stanzahandlers/iq.php @@ -3,7 +3,9 @@ namespace OCA\OJSXC\StanzaHandlers; use OCA\OJSXC\Db\IQRoster; +use OCA\OJSXC\Exceptions\TerminateException; use OCA\OJSXC\IUserProvider; +use OCA\OJSXC\NewContentContainer; use OCP\IConfig; use OCP\IUserManager; use Sabre\Xml\Reader; @@ -39,6 +41,8 @@ class IQ extends StanzaHandler * @param string $host * @param IUserManager $userManager * @param IConfig $config + * @param IUserProvider $userProvider + * @param NewContentContainer $newContentContainer */ public function __construct($userId, $host, IUserManager $userManager, IConfig $config, IUserProvider $userProvider) { @@ -52,6 +56,7 @@ public function __construct($userId, $host, IUserManager $userManager, IConfig $ /** * @param array $stanza * @return IQRoster + * @throws TerminateException TODO */ public function handle(array $stanza) { @@ -60,7 +65,13 @@ public function handle(array $stanza) // if in debug mode we show the own username in the roster for testing $debugMode = $this->config->getSystemValue("debug"); - if ($stanza['value'][0]['name'] === '{jabber:iq:roster}query') { + if ($stanza['value'][0]['name'] === '{http://jabber.org/protocol/disco#items}query' || $stanza['value'][0]['name'] === '{http://jabber.org/protocol/disco#info}query') { + // the disco queries are currently not implemented but these are the first stanzas send to the server so + // they are ideal to terminate the connection if a user is excluded from chatting. + if ($this->userProvider->isUserExcluded($this->userId)) { + throw new TerminateException(); + } + } elseif ($stanza['value'][0]['name'] === '{jabber:iq:roster}query') { $id = $stanza['attributes']['id']; $iqRoster = new IQRoster(); $iqRoster->setType('result'); diff --git a/lib/stanzahandlers/presence.php b/lib/stanzahandlers/presence.php index 97a49c27..efbfd945 100644 --- a/lib/stanzahandlers/presence.php +++ b/lib/stanzahandlers/presence.php @@ -4,6 +4,7 @@ use OCA\OJSXC\Db\MessageMapper; use OCA\OJSXC\Db\PresenceMapper; +use OCA\OJSXC\Exceptions\TerminateException; use Sabre\Xml\Reader; use Sabre\Xml\Writer; use OCA\OJSXC\Db\Presence as PresenceEntity; @@ -47,11 +48,14 @@ public function __construct($userId, $host, PresenceMapper $presenceMapper, Mess * - update the presence in the database * - broadcast the presence * - return the active presence if the type isn't equal to unavailable + * * @param PresenceEntity $presence * @return PresenceEntity[] + * @throws TerminateException */ public function handle(PresenceEntity $presence) { + // update the presence $this->presenceMapper->setPresence($presence);