Philipp Hörist pushed to branch master at gajim / python-nbxmpp
Commits:
-
bd7195cb
by lovetox at 2020-10-18T13:24:20+02:00
2 changed files:
Changes:
| ... | ... | @@ -22,15 +22,16 @@ from gi.repository import Soup |
| 22 | 22 |
from nbxmpp.namespaces import Namespace
|
| 23 | 23 |
from nbxmpp.protocol import Node
|
| 24 | 24 |
from nbxmpp.protocol import Iq
|
| 25 |
-from nbxmpp.protocol import isResultNode
|
|
| 26 | 25 |
from nbxmpp.structs import MuclumbusItem
|
| 27 | 26 |
from nbxmpp.structs import MuclumbusResult
|
| 28 | 27 |
from nbxmpp.const import AnonymityMode
|
| 29 | 28 |
from nbxmpp.modules.dataforms import extend_form
|
| 30 |
-from nbxmpp.util import call_on_response
|
|
| 31 |
-from nbxmpp.util import callback
|
|
| 32 |
-from nbxmpp.util import raise_error
|
|
| 29 |
+from nbxmpp.errors import StanzaError
|
|
| 30 |
+from nbxmpp.errors import MalformedStanzaError
|
|
| 31 |
+from nbxmpp.task import iq_request_task
|
|
| 32 |
+from nbxmpp.task import http_request_task
|
|
| 33 | 33 |
from nbxmpp.modules.base import BaseModule
|
| 34 |
+from nbxmpp.modules.util import finalize
|
|
| 34 | 35 |
|
| 35 | 36 |
|
| 36 | 37 |
# API Documentation
|
| ... | ... | @@ -52,84 +53,58 @@ class Muclumbus(BaseModule): |
| 52 | 53 |
self._proxy_resolver = proxy.get_resolver()
|
| 53 | 54 |
self._soup_session.props.proxy_resolver = self._proxy_resolver
|
| 54 | 55 |
|
| 55 |
- @call_on_response('_parameters_received')
|
|
| 56 |
+ @iq_request_task
|
|
| 56 | 57 |
def request_parameters(self, jid):
|
| 57 |
- query = Iq(to=jid, typ='get')
|
|
| 58 |
- query.addChild(node=Node('search',
|
|
| 59 |
- attrs={'xmlns': Namespace.MUCLUMBUS}))
|
|
| 60 |
- return query
|
|
| 58 |
+ task = yield
|
|
| 61 | 59 |
|
| 62 |
- @callback
|
|
| 63 |
- def _parameters_received(self, stanza):
|
|
| 64 |
- if not isResultNode(stanza):
|
|
| 65 |
- return raise_error(self._log.info, stanza)
|
|
| 60 |
+ response = yield _make_parameter_request(jid)
|
|
| 61 |
+ if response.isError():
|
|
| 62 |
+ raise StanzaError(response)
|
|
| 66 | 63 |
|
| 67 |
- search = stanza.getTag('search', namespace=Namespace.MUCLUMBUS)
|
|
| 64 |
+ search = response.getTag('search', namespace=Namespace.MUCLUMBUS)
|
|
| 68 | 65 |
if search is None:
|
| 69 |
- return raise_error(self._log.warning, stanza, 'stanza-malformed')
|
|
| 66 |
+ raise MalformedStanzaError('search node missing', response)
|
|
| 70 | 67 |
|
| 71 | 68 |
dataform = search.getTag('x', namespace=Namespace.DATA)
|
| 72 | 69 |
if dataform is None:
|
| 73 |
- return raise_error(self._log.warning, stanza, 'stanza-malformed')
|
|
| 70 |
+ raise MalformedStanzaError('dataform node missing', response)
|
|
| 74 | 71 |
|
| 75 | 72 |
self._log.info('Muclumbus parameters received')
|
| 76 |
- return extend_form(node=dataform)
|
|
| 73 |
+ yield finalize(task, extend_form(node=dataform))
|
|
| 77 | 74 |
|
| 78 |
- @call_on_response('_search_received')
|
|
| 75 |
+ @iq_request_task
|
|
| 79 | 76 |
def set_search(self, jid, dataform, items_per_page=50, after=None):
|
| 80 |
- search = Node('search', attrs={'xmlns': Namespace.MUCLUMBUS})
|
|
| 81 |
- search.addChild(node=dataform)
|
|
| 82 |
- rsm = search.addChild('set', namespace=Namespace.RSM)
|
|
| 83 |
- rsm.addChild('max').setData(items_per_page)
|
|
| 84 |
- if after is not None:
|
|
| 85 |
- rsm.addChild('after').setData(after)
|
|
| 86 |
- query = Iq(to=jid, typ='get')
|
|
| 87 |
- query.addChild(node=search)
|
|
| 88 |
- return query
|
|
| 89 |
- |
|
| 90 |
- def set_http_search(self, uri, keywords, after=None,
|
|
| 91 |
- callback=None, user_data=None):
|
|
| 92 |
- search = {'keywords': keywords}
|
|
| 93 |
- if after is not None:
|
|
| 94 |
- search['after'] = after
|
|
| 95 |
- |
|
| 96 |
- message = Soup.Message.new('POST', uri)
|
|
| 97 |
- message.set_request('application/json',
|
|
| 98 |
- Soup.MemoryUse.COPY,
|
|
| 99 |
- json.dumps(search).encode())
|
|
| 77 |
+ _task = yield
|
|
| 100 | 78 |
|
| 101 |
- self._soup_session.queue_message(message,
|
|
| 102 |
- self._http_search_received,
|
|
| 103 |
- callback,
|
|
| 104 |
- user_data)
|
|
| 79 |
+ response = yield _make_search_query(jid,
|
|
| 80 |
+ dataform,
|
|
| 81 |
+ items_per_page,
|
|
| 82 |
+ after)
|
|
| 83 |
+ if response.isError():
|
|
| 84 |
+ raise StanzaError(response)
|
|
| 105 | 85 |
|
| 106 |
- @callback
|
|
| 107 |
- def _search_received(self, stanza):
|
|
| 108 |
- if not isResultNode(stanza):
|
|
| 109 |
- return raise_error(self._log.info, stanza)
|
|
| 110 |
- |
|
| 111 |
- result = stanza.getTag('result', namespace=Namespace.MUCLUMBUS)
|
|
| 86 |
+ result = response.getTag('result', namespace=Namespace.MUCLUMBUS)
|
|
| 112 | 87 |
if result is None:
|
| 113 |
- return raise_error(self._log.warning, stanza, 'stanza-malformed')
|
|
| 88 |
+ raise MalformedStanzaError('result node missing', response)
|
|
| 114 | 89 |
|
| 115 | 90 |
items = result.getTags('item')
|
| 116 | 91 |
if not items:
|
| 117 |
- return MuclumbusResult(first=None,
|
|
| 118 |
- last=None,
|
|
| 119 |
- max=None,
|
|
| 120 |
- end=True,
|
|
| 121 |
- items=[])
|
|
| 92 |
+ yield MuclumbusResult(first=None,
|
|
| 93 |
+ last=None,
|
|
| 94 |
+ max=None,
|
|
| 95 |
+ end=True,
|
|
| 96 |
+ items=[])
|
|
| 122 | 97 |
|
| 123 | 98 |
set_ = result.getTag('set', namespace=Namespace.RSM)
|
| 124 | 99 |
if set_ is None:
|
| 125 |
- return raise_error(self._log.warning, stanza, 'stanza-malformed')
|
|
| 100 |
+ raise MalformedStanzaError('set node missing', response)
|
|
| 126 | 101 |
|
| 127 | 102 |
first = set_.getTagData('first')
|
| 128 | 103 |
last = set_.getTagData('last')
|
| 129 | 104 |
try:
|
| 130 | 105 |
max_ = int(set_.getTagData('max'))
|
| 131 | 106 |
except Exception:
|
| 132 |
- return raise_error(self._log.warning, stanza, 'stanza-malformed')
|
|
| 107 |
+ raise MalformedStanzaError('invalid max value', response)
|
|
| 133 | 108 |
|
| 134 | 109 |
results = []
|
| 135 | 110 |
for item in items:
|
| ... | ... | @@ -152,40 +127,47 @@ class Muclumbus(BaseModule): |
| 152 | 127 |
language=language or '',
|
| 153 | 128 |
is_open=is_open,
|
| 154 | 129 |
anonymity_mode=anonymity_mode))
|
| 155 |
- return MuclumbusResult(first=first,
|
|
| 156 |
- last=last,
|
|
| 157 |
- max=max_,
|
|
| 158 |
- end=len(items) < max_,
|
|
| 159 |
- items=results)
|
|
| 130 |
+ yield MuclumbusResult(first=first,
|
|
| 131 |
+ last=last,
|
|
| 132 |
+ max=max_,
|
|
| 133 |
+ end=len(items) < max_,
|
|
| 134 |
+ items=results)
|
|
| 135 |
+ |
|
| 136 |
+ @http_request_task
|
|
| 137 |
+ def set_http_search(self, uri, keywords, after=None):
|
|
| 138 |
+ _task = yield
|
|
| 139 |
+ |
|
| 140 |
+ search = {'keywords': keywords}
|
|
| 141 |
+ if after is not None:
|
|
| 142 |
+ search['after'] = after
|
|
| 160 | 143 |
|
| 161 |
- def _http_search_received(self, _session, message, callback, user_data):
|
|
| 144 |
+ message = Soup.Message.new('POST', uri)
|
|
| 145 |
+ message.set_request('application/json',
|
|
| 146 |
+ Soup.MemoryUse.COPY,
|
|
| 147 |
+ json.dumps(search).encode())
|
|
| 162 | 148 |
|
| 163 |
- def exec_callback(muclumbus_result):
|
|
| 164 |
- if user_data is None:
|
|
| 165 |
- callback(muclumbus_result)
|
|
| 166 |
- else:
|
|
| 167 |
- callback(muclumbus_result, user_data)
|
|
| 149 |
+ response_message = yield message
|
|
| 168 | 150 |
|
| 169 |
- soup_body = message.get_property('response-body')
|
|
| 151 |
+ soup_body = response_message.get_property('response-body')
|
|
| 170 | 152 |
|
| 171 |
- if message.status_code != 200:
|
|
| 153 |
+ if response_message.status_code != 200:
|
|
| 172 | 154 |
self._log.warning(soup_body.data)
|
| 173 |
- exec_callback(MuclumbusResult(first=None,
|
|
| 174 |
- last=None,
|
|
| 175 |
- max=None,
|
|
| 176 |
- end=True,
|
|
| 177 |
- items=[]))
|
|
| 155 |
+ yield MuclumbusResult(first=None,
|
|
| 156 |
+ last=None,
|
|
| 157 |
+ max=None,
|
|
| 158 |
+ end=True,
|
|
| 159 |
+ items=[])
|
|
| 178 | 160 |
|
| 179 | 161 |
response = json.loads(soup_body.data)
|
| 180 | 162 |
|
| 181 | 163 |
result = response['result']
|
| 182 | 164 |
items = result.get('items')
|
| 183 | 165 |
if items is None:
|
| 184 |
- exec_callback(MuclumbusResult(first=None,
|
|
| 185 |
- last=None,
|
|
| 186 |
- max=None,
|
|
| 187 |
- end=True,
|
|
| 188 |
- items=[]))
|
|
| 166 |
+ yield MuclumbusResult(first=None,
|
|
| 167 |
+ last=None,
|
|
| 168 |
+ max=None,
|
|
| 169 |
+ end=True,
|
|
| 170 |
+ items=[])
|
|
| 189 | 171 |
|
| 190 | 172 |
results = []
|
| 191 | 173 |
for item in items:
|
| ... | ... | @@ -203,8 +185,27 @@ class Muclumbus(BaseModule): |
| 203 | 185 |
is_open=item['is_open'],
|
| 204 | 186 |
anonymity_mode=anonymity_mode))
|
| 205 | 187 |
|
| 206 |
- exec_callback(MuclumbusResult(first=None,
|
|
| 207 |
- last=result['last'],
|
|
| 208 |
- max=None,
|
|
| 209 |
- end=not result['more'],
|
|
| 210 |
- items=results))
|
|
| 188 |
+ yield MuclumbusResult(first=None,
|
|
| 189 |
+ last=result['last'],
|
|
| 190 |
+ max=None,
|
|
| 191 |
+ end=not result['more'],
|
|
| 192 |
+ items=results)
|
|
| 193 |
+ |
|
| 194 |
+ |
|
| 195 |
+def _make_parameter_request(jid):
|
|
| 196 |
+ query = Iq(to=jid, typ='get')
|
|
| 197 |
+ query.addChild(node=Node('search',
|
|
| 198 |
+ attrs={'xmlns': Namespace.MUCLUMBUS}))
|
|
| 199 |
+ return query
|
|
| 200 |
+ |
|
| 201 |
+ |
|
| 202 |
+def _make_search_query(jid, dataform, items_per_page=50, after=None):
|
|
| 203 |
+ search = Node('search', attrs={'xmlns': Namespace.MUCLUMBUS})
|
|
| 204 |
+ search.addChild(node=dataform)
|
|
| 205 |
+ rsm = search.addChild('set', namespace=Namespace.RSM)
|
|
| 206 |
+ rsm.addChild('max').setData(items_per_page)
|
|
| 207 |
+ if after is not None:
|
|
| 208 |
+ rsm.addChild('after').setData(after)
|
|
| 209 |
+ query = Iq(to=jid, typ='get')
|
|
| 210 |
+ query.addChild(node=search)
|
|
| 211 |
+ return query
|
| ... | ... | @@ -21,6 +21,8 @@ import logging |
| 21 | 21 |
from enum import IntEnum
|
| 22 | 22 |
from functools import wraps
|
| 23 | 23 |
|
| 24 |
+from gi.repository import Soup
|
|
| 25 |
+ |
|
| 24 | 26 |
from nbxmpp.errors import is_error
|
| 25 | 27 |
from nbxmpp.errors import CancelledError
|
| 26 | 28 |
from nbxmpp.simplexml import Node
|
| ... | ... | @@ -62,20 +64,33 @@ class TaskState(IntEnum): |
| 62 | 64 |
return self == TaskState.CANCELLED
|
| 63 | 65 |
|
| 64 | 66 |
|
| 67 |
+def _setup_task(task, client, callback, user_data):
|
|
| 68 |
+ client.add_task(task)
|
|
| 69 |
+ task.set_finalize_func(client.remove_task)
|
|
| 70 |
+ task.set_user_data(user_data)
|
|
| 71 |
+ if callback is not None:
|
|
| 72 |
+ task.add_done_callback(callback)
|
|
| 73 |
+ task.start()
|
|
| 74 |
+ return task
|
|
| 75 |
+ |
|
| 76 |
+ |
|
| 65 | 77 |
def iq_request_task(func):
|
| 66 | 78 |
@wraps(func)
|
| 67 |
- def func_wrapper(self, *args,
|
|
| 68 |
- callback=None, user_data=None, **kwargs):
|
|
| 79 |
+ def func_wrapper(self, *args, callback=None, user_data=None, **kwargs):
|
|
| 69 | 80 |
task = IqRequestTask(func(self, *args, **kwargs),
|
| 70 | 81 |
self._log,
|
| 71 | 82 |
self._client)
|
| 72 |
- self._client.add_task(task)
|
|
| 73 |
- task.set_finalize_func(self._client.remove_task)
|
|
| 74 |
- task.set_user_data(user_data)
|
|
| 75 |
- if callback is not None:
|
|
| 76 |
- task.add_done_callback(callback)
|
|
| 77 |
- task.start()
|
|
| 78 |
- return task
|
|
| 83 |
+ return _setup_task(task, self._client, callback, user_data)
|
|
| 84 |
+ return func_wrapper
|
|
| 85 |
+ |
|
| 86 |
+ |
|
| 87 |
+def http_request_task(func):
|
|
| 88 |
+ @wraps(func)
|
|
| 89 |
+ def func_wrapper(self, *args, callback=None, user_data=None, **kwargs):
|
|
| 90 |
+ task = HTTPRequestTask(func(self, *args, **kwargs),
|
|
| 91 |
+ self._log,
|
|
| 92 |
+ self._soup_session)
|
|
| 93 |
+ return _setup_task(task, self._client, callback, user_data)
|
|
| 79 | 94 |
return func_wrapper
|
| 80 | 95 |
|
| 81 | 96 |
|
| ... | ... | @@ -287,3 +302,28 @@ class IqRequestTask(Task): |
| 287 | 302 |
def _finalize(self):
|
| 288 | 303 |
self._client = None
|
| 289 | 304 |
super()._finalize()
|
| 305 |
+ |
|
| 306 |
+ |
|
| 307 |
+class HTTPRequestTask(Task):
|
|
| 308 |
+ |
|
| 309 |
+ '''
|
|
| 310 |
+ A Task for running HTTP requests
|
|
| 311 |
+ |
|
| 312 |
+ '''
|
|
| 313 |
+ |
|
| 314 |
+ _process_types = (Soup.Message,)
|
|
| 315 |
+ |
|
| 316 |
+ def __init__(self, gen, logger, session):
|
|
| 317 |
+ super().__init__(gen, logger)
|
|
| 318 |
+ self._session = session
|
|
| 319 |
+ |
|
| 320 |
+ def _run_async(self, message):
|
|
| 321 |
+ self._session.queue_message(message, self._async_finished, None)
|
|
| 322 |
+ |
|
| 323 |
+ def _async_finished(self, _session, message, _user_data):
|
|
| 324 |
+ if self._state != TaskState.CANCELLED:
|
|
| 325 |
+ self._next_step(message)
|
|
| 326 |
+ |
|
| 327 |
+ def _finalize(self):
|
|
| 328 |
+ self._session = None
|
|
| 329 |
+ super()._finalize()
|