| ... |
... |
@@ -18,19 +18,18 @@ |
|
18
|
18
|
|
|
19
|
19
|
from nbxmpp.protocol import JID
|
|
20
|
20
|
from nbxmpp.protocol import Iq
|
|
21
|
|
-from nbxmpp.protocol import isResultNode
|
|
22
|
21
|
from nbxmpp.protocol import Node
|
|
23
|
22
|
from nbxmpp.namespaces import Namespace
|
|
24
|
23
|
from nbxmpp.structs import MAMQueryData
|
|
25
|
24
|
from nbxmpp.structs import MAMPreferencesData
|
|
26
|
|
-from nbxmpp.structs import CommonResult
|
|
27
|
|
-from nbxmpp.util import call_on_response
|
|
28
|
|
-from nbxmpp.util import callback
|
|
29
|
|
-from nbxmpp.util import raise_error
|
|
|
25
|
+from nbxmpp.task import iq_request_task
|
|
|
26
|
+from nbxmpp.errors import StanzaError
|
|
|
27
|
+from nbxmpp.errors import MalformedStanzaError
|
|
|
28
|
+from nbxmpp.modules.base import BaseModule
|
|
30
|
29
|
from nbxmpp.modules.rsm import parse_rsm
|
|
31
|
30
|
from nbxmpp.modules.dataforms import SimpleDataForm
|
|
32
|
31
|
from nbxmpp.modules.dataforms import create_field
|
|
33
|
|
-from nbxmpp.modules.base import BaseModule
|
|
|
32
|
+from nbxmpp.modules.util import process_response
|
|
34
|
33
|
|
|
35
|
34
|
|
|
36
|
35
|
class MAM(BaseModule):
|
| ... |
... |
@@ -40,7 +39,7 @@ class MAM(BaseModule): |
|
40
|
39
|
self._client = client
|
|
41
|
40
|
self.handlers = []
|
|
42
|
41
|
|
|
43
|
|
- @call_on_response('_query_result')
|
|
|
42
|
+ @iq_request_task
|
|
44
|
43
|
def make_query(self,
|
|
45
|
44
|
jid,
|
|
46
|
45
|
queryid=None,
|
| ... |
... |
@@ -50,166 +49,155 @@ class MAM(BaseModule): |
|
50
|
49
|
after=None,
|
|
51
|
50
|
max_=70):
|
|
52
|
51
|
|
|
53
|
|
- iq = Iq(typ='set', to=jid, queryNS=Namespace.MAM_2)
|
|
54
|
|
- if queryid is not None:
|
|
55
|
|
- iq.getQuery().setAttr('queryid', queryid)
|
|
56
|
|
-
|
|
57
|
|
- payload = [
|
|
58
|
|
- self._make_query_form(start, end, with_),
|
|
59
|
|
- self._make_rsm_query(max_, after)
|
|
60
|
|
- ]
|
|
61
|
|
-
|
|
62
|
|
- iq.setQueryPayload(payload)
|
|
63
|
|
- return iq
|
|
64
|
|
-
|
|
65
|
|
- @staticmethod
|
|
66
|
|
- def _make_query_form(start, end, with_):
|
|
67
|
|
- fields = [
|
|
68
|
|
- create_field(typ='hidden', var='FORM_TYPE', value=Namespace.MAM_2)
|
|
69
|
|
- ]
|
|
70
|
|
-
|
|
71
|
|
- if start:
|
|
72
|
|
- fields.append(create_field(
|
|
73
|
|
- typ='text-single',
|
|
74
|
|
- var='start',
|
|
75
|
|
- value=start.strftime('%Y-%m-%dT%H:%M:%SZ')))
|
|
76
|
|
-
|
|
77
|
|
- if end:
|
|
78
|
|
- fields.append(create_field(
|
|
79
|
|
- typ='text-single',
|
|
80
|
|
- var='end',
|
|
81
|
|
- value=end.strftime('%Y-%m-%dT%H:%M:%SZ')))
|
|
82
|
|
-
|
|
83
|
|
- if with_:
|
|
84
|
|
- fields.append(create_field(
|
|
85
|
|
- typ='jid-single',
|
|
86
|
|
- var='with',
|
|
87
|
|
- value=with_))
|
|
88
|
|
-
|
|
89
|
|
- return SimpleDataForm(type_='submit', fields=fields)
|
|
90
|
|
-
|
|
91
|
|
- @staticmethod
|
|
92
|
|
- def _make_rsm_query(max_, after):
|
|
93
|
|
- rsm_set = Node('set', attrs={'xmlns': Namespace.RSM})
|
|
94
|
|
- if max_ is not None:
|
|
95
|
|
- rsm_set.setTagData('max', max_)
|
|
96
|
|
- if after is not None:
|
|
97
|
|
- rsm_set.setTagData('after', after)
|
|
98
|
|
- return rsm_set
|
|
99
|
|
-
|
|
100
|
|
- @callback
|
|
101
|
|
- def _query_result(self, stanza):
|
|
102
|
|
- if not isResultNode(stanza):
|
|
103
|
|
- return raise_error(self._log.info, stanza)
|
|
104
|
|
-
|
|
105
|
|
- jid = stanza.getFrom()
|
|
106
|
|
- fin = stanza.getTag('fin', namespace=Namespace.MAM_2)
|
|
|
52
|
+ _task = yield
|
|
|
53
|
+
|
|
|
54
|
+ response = yield _make_request(jid, queryid,
|
|
|
55
|
+ start, end, with_, after, max_)
|
|
|
56
|
+ if response.isError():
|
|
|
57
|
+ raise StanzaError(response)
|
|
|
58
|
+
|
|
|
59
|
+ jid = response.getFrom()
|
|
|
60
|
+ fin = response.getTag('fin', namespace=Namespace.MAM_2)
|
|
107
|
61
|
if fin is None:
|
|
108
|
|
- return raise_error(self._log.warning,
|
|
109
|
|
- stanza,
|
|
110
|
|
- 'stanza-malformed',
|
|
111
|
|
- 'No fin node found')
|
|
|
62
|
+ raise MalformedStanzaError('fin node missing', response)
|
|
112
|
63
|
|
|
113
|
64
|
rsm = parse_rsm(fin)
|
|
114
|
65
|
if rsm is None:
|
|
115
|
|
- return raise_error(self._log.warning,
|
|
116
|
|
- stanza,
|
|
117
|
|
- 'stanza-malformed',
|
|
118
|
|
- 'rsm set missing')
|
|
|
66
|
+ raise MalformedStanzaError('rsm set missing', response)
|
|
119
|
67
|
|
|
120
|
68
|
complete = fin.getAttr('complete') == 'true'
|
|
121
|
69
|
if not complete:
|
|
122
|
70
|
if rsm.first is None or rsm.last is None:
|
|
123
|
|
- return raise_error(self._log.warning,
|
|
124
|
|
- stanza,
|
|
125
|
|
- 'stanza-malformed',
|
|
126
|
|
- 'missing first or last element')
|
|
|
71
|
+ raise MalformedStanzaError('first or last element missing',
|
|
|
72
|
+ response)
|
|
127
|
73
|
|
|
128
|
|
- return MAMQueryData(jid=jid,
|
|
129
|
|
- complete=complete,
|
|
130
|
|
- rsm=rsm)
|
|
|
74
|
+ yield MAMQueryData(jid=jid,
|
|
|
75
|
+ complete=complete,
|
|
|
76
|
+ rsm=rsm)
|
|
131
|
77
|
|
|
132
|
|
- @call_on_response('_preferences_result')
|
|
|
78
|
+ @iq_request_task
|
|
133
|
79
|
def request_preferences(self):
|
|
134
|
|
- iq = Iq('get', queryNS=Namespace.MAM_2)
|
|
135
|
|
- iq.setQuery('prefs')
|
|
136
|
|
- return iq
|
|
|
80
|
+ _task = yield
|
|
137
|
81
|
|
|
138
|
|
- @callback
|
|
139
|
|
- def _preferences_result(self, stanza):
|
|
140
|
|
- if not isResultNode(stanza):
|
|
141
|
|
- return raise_error(self._log.info, stanza)
|
|
|
82
|
+ response = yield _make_pref_request()
|
|
|
83
|
+ if response.isError():
|
|
|
84
|
+ raise StanzaError(response)
|
|
142
|
85
|
|
|
143
|
|
- prefs = stanza.getTag('prefs', namespace=Namespace.MAM_2)
|
|
|
86
|
+ prefs = response.getTag('prefs', namespace=Namespace.MAM_2)
|
|
144
|
87
|
if prefs is None:
|
|
145
|
|
- return raise_error(self._log.warning,
|
|
146
|
|
- stanza,
|
|
147
|
|
- 'stanza-malformed',
|
|
148
|
|
- 'No prefs node found')
|
|
|
88
|
+ raise MalformedStanzaError('prefs node missing', response)
|
|
149
|
89
|
|
|
150
|
90
|
default = prefs.getAttr('default')
|
|
151
|
91
|
if default is None:
|
|
152
|
|
- return raise_error(self._log.warning,
|
|
153
|
|
- stanza,
|
|
154
|
|
- 'stanza-malformed',
|
|
155
|
|
- 'No default attr found')
|
|
|
92
|
+ raise MalformedStanzaError('default attr missing', response)
|
|
156
|
93
|
|
|
157
|
94
|
always_node = prefs.getTag('always')
|
|
158
|
95
|
if always_node is None:
|
|
159
|
|
- return raise_error(self._log.warning,
|
|
160
|
|
- stanza,
|
|
161
|
|
- 'stanza-malformed',
|
|
162
|
|
- 'No always node found')
|
|
|
96
|
+ raise MalformedStanzaError('always node missing', response)
|
|
163
|
97
|
|
|
164
|
|
- always = self._get_preference_jids(always_node)
|
|
|
98
|
+ always = _get_preference_jids(always_node)
|
|
165
|
99
|
|
|
166
|
100
|
never_node = prefs.getTag('never')
|
|
167
|
101
|
if never_node is None:
|
|
168
|
|
- return raise_error(self._log.warning,
|
|
169
|
|
- stanza,
|
|
170
|
|
- 'stanza-malformed',
|
|
171
|
|
- 'No never node found')
|
|
172
|
|
-
|
|
173
|
|
- never = self._get_preference_jids(never_node)
|
|
174
|
|
- return MAMPreferencesData(default=default,
|
|
175
|
|
- always=always,
|
|
176
|
|
- never=never)
|
|
177
|
|
-
|
|
178
|
|
- def _get_preference_jids(self, node):
|
|
179
|
|
- jids = []
|
|
180
|
|
- for item in node.getTags('jid'):
|
|
181
|
|
- jid = item.getData()
|
|
182
|
|
- if not jid:
|
|
183
|
|
- continue
|
|
184
|
|
-
|
|
185
|
|
- try:
|
|
186
|
|
- jid = JID.from_string(jid)
|
|
187
|
|
- except Exception:
|
|
188
|
|
- self._log.warning('Invalid jid found in preferences: %s',
|
|
189
|
|
- jid)
|
|
190
|
|
- jids.append(jid)
|
|
191
|
|
- return jids
|
|
192
|
|
-
|
|
193
|
|
- @call_on_response('_default_response')
|
|
|
102
|
+ raise MalformedStanzaError('never node missing', response)
|
|
|
103
|
+
|
|
|
104
|
+ never = _get_preference_jids(never_node)
|
|
|
105
|
+ yield MAMPreferencesData(default=default,
|
|
|
106
|
+ always=always,
|
|
|
107
|
+ never=never)
|
|
|
108
|
+
|
|
|
109
|
+ @iq_request_task
|
|
194
|
110
|
def set_preferences(self, default, always, never):
|
|
|
111
|
+ _task = yield
|
|
|
112
|
+
|
|
195
|
113
|
if default not in ('always', 'never', 'roster'):
|
|
196
|
114
|
raise ValueError('Wrong default preferences type')
|
|
197
|
115
|
|
|
198
|
|
- iq = Iq(typ='set')
|
|
199
|
|
- prefs = iq.addChild(name='prefs',
|
|
200
|
|
- namespace=Namespace.MAM_2,
|
|
201
|
|
- attrs={'default': default})
|
|
202
|
|
- always_node = prefs.addChild(name='always')
|
|
203
|
|
- never_node = prefs.addChild(name='never')
|
|
204
|
|
- for jid in always:
|
|
205
|
|
- always_node.addChild(name='jid').setData(jid)
|
|
206
|
|
-
|
|
207
|
|
- for jid in never:
|
|
208
|
|
- never_node.addChild(name='jid').setData(jid)
|
|
209
|
|
- return iq
|
|
210
|
|
-
|
|
211
|
|
- @callback
|
|
212
|
|
- def _default_response(self, stanza):
|
|
213
|
|
- if not isResultNode(stanza):
|
|
214
|
|
- return raise_error(self._log.info, stanza)
|
|
215
|
|
- return CommonResult(jid=stanza.getFrom())
|
|
|
116
|
+ response = yield _make_set_pref_request(default, always, never)
|
|
|
117
|
+ yield process_response(response)
|
|
|
118
|
+
|
|
|
119
|
+
|
|
|
120
|
+def _make_query_form(start, end, with_):
|
|
|
121
|
+ fields = [
|
|
|
122
|
+ create_field(typ='hidden', var='FORM_TYPE', value=Namespace.MAM_2)
|
|
|
123
|
+ ]
|
|
|
124
|
+
|
|
|
125
|
+ if start:
|
|
|
126
|
+ fields.append(create_field(
|
|
|
127
|
+ typ='text-single',
|
|
|
128
|
+ var='start',
|
|
|
129
|
+ value=start.strftime('%Y-%m-%dT%H:%M:%SZ')))
|
|
|
130
|
+
|
|
|
131
|
+ if end:
|
|
|
132
|
+ fields.append(create_field(
|
|
|
133
|
+ typ='text-single',
|
|
|
134
|
+ var='end',
|
|
|
135
|
+ value=end.strftime('%Y-%m-%dT%H:%M:%SZ')))
|
|
|
136
|
+
|
|
|
137
|
+ if with_:
|
|
|
138
|
+ fields.append(create_field(
|
|
|
139
|
+ typ='jid-single',
|
|
|
140
|
+ var='with',
|
|
|
141
|
+ value=with_))
|
|
|
142
|
+
|
|
|
143
|
+ return SimpleDataForm(type_='submit', fields=fields)
|
|
|
144
|
+
|
|
|
145
|
+
|
|
|
146
|
+def _make_rsm_query(max_, after):
|
|
|
147
|
+ rsm_set = Node('set', attrs={'xmlns': Namespace.RSM})
|
|
|
148
|
+ if max_ is not None:
|
|
|
149
|
+ rsm_set.setTagData('max', max_)
|
|
|
150
|
+ if after is not None:
|
|
|
151
|
+ rsm_set.setTagData('after', after)
|
|
|
152
|
+ return rsm_set
|
|
|
153
|
+
|
|
|
154
|
+
|
|
|
155
|
+def _make_request(jid, queryid, start, end, with_, after, max_):
|
|
|
156
|
+ iq = Iq(typ='set', to=jid, queryNS=Namespace.MAM_2)
|
|
|
157
|
+ if queryid is not None:
|
|
|
158
|
+ iq.getQuery().setAttr('queryid', queryid)
|
|
|
159
|
+
|
|
|
160
|
+ payload = [
|
|
|
161
|
+ _make_query_form(start, end, with_),
|
|
|
162
|
+ _make_rsm_query(max_, after)
|
|
|
163
|
+ ]
|
|
|
164
|
+
|
|
|
165
|
+ iq.setQueryPayload(payload)
|
|
|
166
|
+ return iq
|
|
|
167
|
+
|
|
|
168
|
+
|
|
|
169
|
+def _make_pref_request():
|
|
|
170
|
+ iq = Iq('get', queryNS=Namespace.MAM_2)
|
|
|
171
|
+ iq.setQuery('prefs')
|
|
|
172
|
+ return iq
|
|
|
173
|
+
|
|
|
174
|
+
|
|
|
175
|
+def _get_preference_jids(node):
|
|
|
176
|
+ jids = []
|
|
|
177
|
+ for item in node.getTags('jid'):
|
|
|
178
|
+ jid = item.getData()
|
|
|
179
|
+ if not jid:
|
|
|
180
|
+ continue
|
|
|
181
|
+
|
|
|
182
|
+ try:
|
|
|
183
|
+ jid = JID.from_string(jid)
|
|
|
184
|
+ except Exception:
|
|
|
185
|
+ continue
|
|
|
186
|
+
|
|
|
187
|
+ jids.append(jid)
|
|
|
188
|
+ return jids
|
|
|
189
|
+
|
|
|
190
|
+
|
|
|
191
|
+def _make_set_pref_request(default, always, never):
|
|
|
192
|
+ iq = Iq(typ='set')
|
|
|
193
|
+ prefs = iq.addChild(name='prefs',
|
|
|
194
|
+ namespace=Namespace.MAM_2,
|
|
|
195
|
+ attrs={'default': default})
|
|
|
196
|
+ always_node = prefs.addChild(name='always')
|
|
|
197
|
+ never_node = prefs.addChild(name='never')
|
|
|
198
|
+ for jid in always:
|
|
|
199
|
+ always_node.addChild(name='jid').setData(jid)
|
|
|
200
|
+
|
|
|
201
|
+ for jid in never:
|
|
|
202
|
+ never_node.addChild(name='jid').setData(jid)
|
|
|
203
|
+ return iq
|