1# Copyright (C) 2018 Philipp Hörist <philipp AT hoerist.com> 2# 3# This file is part of nbxmpp. 4# 5# This program is free software; you can redistribute it and/or 6# modify it under the terms of the GNU General Public License 7# as published by the Free Software Foundation; either version 3 8# of the License, or (at your option) any later version. 9# 10# This program is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with this program; If not, see <http://www.gnu.org/licenses/>. 17 18from nbxmpp.namespaces import Namespace 19from nbxmpp.protocol import Iq 20from nbxmpp.protocol import JID 21from nbxmpp.modules.base import BaseModule 22from nbxmpp.errors import StanzaError 23from nbxmpp.errors import MalformedStanzaError 24from nbxmpp.task import iq_request_task 25from nbxmpp.structs import BlockingPush 26from nbxmpp.structs import StanzaHandler 27from nbxmpp.modules.util import process_response 28 29 30class Blocking(BaseModule): 31 def __init__(self, client): 32 BaseModule.__init__(self, client) 33 34 self._client = client 35 self.handlers = [ 36 StanzaHandler(name='iq', 37 priority=15, 38 callback=self._process_blocking_push, 39 typ='set', 40 ns=Namespace.BLOCKING), 41 ] 42 43 @iq_request_task 44 def request_blocking_list(self): 45 _task = yield 46 47 result = yield _make_blocking_list_request() 48 if result.isError(): 49 raise StanzaError(result) 50 51 blocklist = result.getTag('blocklist', namespace=Namespace.BLOCKING) 52 if blocklist is None: 53 raise MalformedStanzaError('blocklist node missing', result) 54 55 blocked = [] 56 for item in blocklist.getTags('item'): 57 blocked.append(item.getAttr('jid')) 58 59 self._log.info('Received blocking list: %s', blocked) 60 yield blocked 61 62 @iq_request_task 63 def block(self, jids, report=None): 64 _task = yield 65 66 self._log.info('Block: %s', jids) 67 68 response = yield _make_block_request(jids, report) 69 yield process_response(response) 70 71 @iq_request_task 72 def unblock(self, jids): 73 _task = yield 74 75 self._log.info('Unblock: %s', jids) 76 77 response = yield _make_unblock_request(jids) 78 yield process_response(response) 79 80 @staticmethod 81 def _process_blocking_push(client, stanza, properties): 82 unblock = stanza.getTag('unblock', namespace=Namespace.BLOCKING) 83 if unblock is not None: 84 properties.blocking = _parse_push(unblock) 85 return 86 87 block = stanza.getTag('block', namespace=Namespace.BLOCKING) 88 if block is not None: 89 properties.blocking = _parse_push(block) 90 91 reply = stanza.buildSimpleReply('result') 92 client.send_stanza(reply) 93 94 95def _make_blocking_list_request(): 96 iq = Iq('get', Namespace.BLOCKING) 97 iq.setQuery('blocklist') 98 return iq 99 100 101def _make_block_request(jids, report): 102 iq = Iq('set', Namespace.BLOCKING) 103 query = iq.setQuery(name='block') 104 for jid in jids: 105 item = query.addChild(name='item', attrs={'jid': jid}) 106 if report in ('spam', 'abuse'): 107 action = item.addChild(name='report', 108 namespace=Namespace.REPORTING) 109 action.setTag(report) 110 return iq 111 112 113def _make_unblock_request(jids): 114 iq = Iq('set', Namespace.BLOCKING) 115 query = iq.setQuery(name='unblock') 116 for jid in jids: 117 query.addChild(name='item', attrs={'jid': jid}) 118 return iq 119 120 121def _parse_push(node): 122 items = node.getTags('item') 123 if not items: 124 return BlockingPush(block=[], unblock=[], unblock_all=True) 125 126 jids = [] 127 for item in items: 128 jid = item.getAttr('jid') 129 if not jid: 130 continue 131 132 try: 133 jid = JID.from_string(jid) 134 except Exception: 135 continue 136 137 jids.append(jid) 138 139 140 block, unblock = [], [] 141 if node.getName() == 'block': 142 block = jids 143 else: 144 unblock = jids 145 146 return BlockingPush(block=block, unblock=unblock, unblock_all=False) 147