Mercurial > sat_tmp
changeset 24:0640d72d6841
tmp (wokkel.mam): cleaning and bug fix:
- renamed MAMQueryRequest to MAMQuery
- use of MAMQuery in queryArchive instead of separate arguments
- fixed bad use of subprotocols.IQHandlerMixing.handleRequest
- changed order of arguments in BuildForm to have more often used first
- renamed parse to fromElement for coherency
- added IMAMService interface
- MAMService requestClass can be changed (it is specified in _request_class class attribute
- use new-style classes where it make sense
- MAMRequest.fromElement parse a <query/> parent instead of a <query/> directly
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 05 Jan 2016 23:20:22 +0100 |
parents | 777b4e63fc8a |
children | 4816f7d55367 |
files | wokkel/mam.py |
diffstat | 1 files changed, 84 insertions(+), 82 deletions(-) [+] |
line wrap: on
line diff
--- a/wokkel/mam.py Tue Jan 05 23:20:22 2016 +0100 +++ b/wokkel/mam.py Tue Jan 05 23:20:22 2016 +0100 @@ -25,18 +25,21 @@ U{XEP-0313<http://xmpp.org/extensions/xep-0313.html>}. """ -from dateutil.tz import tzutc +from dateutil import tz -from zope.interface import Interface, implements +from zope.interface import implements +from zope.interface import Interface -from twisted.words.protocols.jabber.xmlstream import IQ, toResponse +from twisted.words.protocols.jabber import xmlstream from twisted.words.xish import domish from twisted.words.protocols.jabber import jid from twisted.internet import defer from twisted.python import log -from wokkel.subprotocols import IQHandlerMixin, XMPPHandler -from wokkel import disco, data_form, delay +from wokkel import subprotocols +from wokkel import disco +from wokkel import data_form +from wokkel import delay import rsm @@ -50,13 +53,11 @@ # TODO: add the tests! - class MAMError(Exception): """ MAM error. """ - class Unsupported(MAMError): def __init__(self, feature, text=None): self.feature = feature @@ -71,7 +72,7 @@ return message -class MAMQueryRequest(): +class MAMRequest(object): """ A Message Archive Management <query/> request. @@ -98,25 +99,27 @@ self.query_id = query_id @classmethod - def parse(cls, element): + def fromElement(cls, element): """Parse the DOM representation of a MAM <query/> request. - @param element: MAM <query/> request element. + @param element: element containing a MAM <query/>. @type element: L{Element<twisted.words.xish.domish.Element>} - @return: MAMQueryRequest instance. - @rtype: L{MAMQueryRequest} + @return: MAMRequest instance. + @rtype: L{MAMRequest} """ - if element.uri != NS_MAM or element.name != 'query': - raise MAMError('Element provided is not a MAM <query/> request') - form = data_form.findForm(element, NS_MAM) try: - rsm_request = rsm.RSMRequest.parse(element) + query = element.elements(NS_MAM, 'query').next() + except StopIteration: + raise MAMError("Can't find MAM <query/> in element") + form = data_form.findForm(query, NS_MAM) + try: + rsm_request = rsm.RSMRequest.fromElement(query) except rsm.RSMNotFoundError: rsm_request = None - node = element.getAttribute('node') - query_id = element.getAttribute('queryid') - return MAMQueryRequest(form, rsm_request, node, query_id) + node = query.getAttribute('node') + query_id = query.getAttribute('queryid') + return MAMRequest(form, rsm_request, node, query_id) def toElement(self): """ @@ -151,7 +154,7 @@ return mam_elt -class MAMPrefs(): +class MAMPrefs(object): """ A Message Archive Management <prefs/> request. @@ -180,7 +183,7 @@ self.never = never @classmethod - def parse(cls, prefs_elt): + def fromElement(cls, prefs_elt): """Parse the DOM representation of a MAM <prefs/> request. @param prefs_elt: MAM <prefs/> request element. @@ -241,45 +244,36 @@ return mam_elt -class MAMClient(XMPPHandler): +class MAMClient(subprotocols.XMPPHandler): """ MAM client. This handler implements the protocol for sending out MAM requests. """ - def queryArchive(self, service=None, form=None, rsm=None, node=None, sender=None, query_id=None): + def queryArchive(self, mam_query, service=None, sender=None): """Query a user, MUC or pubsub archive. - @param service: Entity offering the MAM service (None for user archives). - @type service: L{JID<twisted.words.protocols.jabber.jid.JID>} - - @param form: Data Form to filter the request. - @type form: L{Form<wokkel.data_form.Form>} + @param mam_query: query to use + @type form: L{MAMRequest} - @param rsm: RSM request instance. - @type rsm: L{RSMRequest<wokkel.rsm.RSMRequest>} - - @param node: Pubsub node to query, or None if inappropriate. - @type node: C{unicode} + @param service: Entity offering the MAM service (None for user server). + @type service: L{JID<twisted.words.protocols.jabber.jid.JID>} @param sender: Optional sender address. @type sender: L{JID<twisted.words.protocols.jabber.jid.JID>} - @param query_id: Optional query id - @type query_id: C{unicode} - @return: A deferred that fires upon receiving a response. @rtype: L{Deferred<twisted.internet.defer.Deferred>} """ - iq = IQ(self.xmlstream, 'set') - MAMQueryRequest(form, rsm, node, query_id).render(iq) + iq = xmlstream.IQ(self.xmlstream, 'set') + mam_query.render(iq) if sender is not None: iq['from'] = unicode(sender) return iq.send(to=service.full() if service else None) def queryFields(self, service=None, sender=None): - """Ask the server about additional supported fields. + """Ask the server about supported fields. @param service: Entity offering the MAM service (None for user archives). @type service: L{JID<twisted.words.protocols.jabber.jid.JID>} @@ -291,8 +285,8 @@ @rtype: L{Deferred<twisted.internet.defer.Deferred>} """ # http://xmpp.org/extensions/xep-0313.html#query-form - iq = IQ(self.xmlstream, 'get') - MAMQueryRequest().render(iq) + iq = xmlstream.IQ(self.xmlstream, 'get') + MAMRequest().render(iq) if sender is not None: iq['from'] = unicode(sender) d = iq.send(to=service.full() if service else None) @@ -313,7 +307,7 @@ @rtype: L{Deferred<twisted.internet.defer.Deferred>} """ # http://xmpp.org/extensions/xep-0313.html#prefs - iq = IQ(self.xmlstream, 'get') + iq = xmlstream.IQ(self.xmlstream, 'get') MAMPrefs().render(iq) if sender is not None: iq['from'] = unicode(sender) @@ -342,7 +336,7 @@ """ # http://xmpp.org/extensions/xep-0313.html#prefs assert default is not None - iq = IQ(self.xmlstream, 'set') + iq = xmlstream.IQ(self.xmlstream, 'set') MAMPrefs(default, always, never).render(iq) if sender is not None: iq['from'] = unicode(sender) @@ -355,7 +349,7 @@ """ @param mam: The MAM <query/> request. - @type mam: L{MAMQueryReques<wokkel.mam.MAMQueryRequest>} + @type mam: L{MAMQueryReques<wokkel.mam.MAMRequest>} @param requestor: JID of the requestor. @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>} @@ -387,15 +381,29 @@ @rtype: L{wokkel.mam.MAMPrefs} """ +class IMAMService(Interface): + """ + Interface for XMPP MAM service. + """ -class MAMService(XMPPHandler, IQHandlerMixin): + def addFilter(self, field): + """ + Add a new filter for querying MAM archive. + + @param field: data form field of the filter + @type field: L{Form<wokkel.data_form.Field>} + """ + + +class MAMService(subprotocols.XMPPHandler, subprotocols.IQHandlerMixin): """ Protocol implementation for a MAM service. This handler waits for XMPP Ping requests and sends a response. """ + implements(IMAMService, disco.IDisco) - implements(disco.IDisco) + _request_class = MAMRequest iqHandlers = {FIELDS_REQUEST: '_onFieldsRequest', ARCHIVE_REQUEST: '_onArchiveRequest', @@ -454,11 +462,10 @@ This immediately replies with a result response. """ - response = toResponse(iq, 'result') - query = response.addElement((NS_MAM, 'query')) - query.addChild(buildForm('form', extra=self.extra_fields).toElement()) - self.xmlstream.send(response) iq.handled = True + query = domish.Element((NS_MAM, 'query')) + query.addChild(buildForm(extra_fields=self.extra_fields).toElement(), formType='form') + return query def _onArchiveRequest(self, iq): """ @@ -468,7 +475,8 @@ @return: A tuple with list of message data (id, element, data) and RSM element @rtype: C{tuple} """ - mam_ = MAMQueryRequest.parse(iq.query) + iq.handled = True + mam_ = self._request_class.fromElement(iq) requestor = jid.JID(iq['from']) # remove unsupported filters @@ -481,14 +489,12 @@ for key in unsupported_fields: del mam_.form.fields[key] - def forward_message(id_, elt, date): + def forwardMessage(id_, elt, date): msg = domish.Element((None, 'message')) msg['to'] = iq['from'] result = msg.addElement((NS_MAM, 'result')) - try: - result['queryid'] = iq.query['queryid'] - except KeyError: - pass + if mam_.query_id is not None: + result['queryid'] = mam_.query_id result['id'] = id_ forward = result.addElement((NS_FORWARD, 'forwarded')) forward.addChild(delay.Delay(date).toElement()) @@ -498,18 +504,17 @@ def cb(result): msg_data, rsm_elt = result for data in msg_data: - forward_message(*data) + forwardMessage(*data) - response = toResponse(iq, 'result') - fin = response.addElement((NS_MAM, 'fin')) + fin_elt = domish.Element((NS_MAM, 'fin')) if rsm_elt is not None: - fin.addChild(rsm_elt) - self.xmlstream.send(response) + fin_elt.addChild(rsm_elt) + return fin_elt d = defer.maybeDeferred(self.resource.onArchiveRequest, mam_, requestor) d.addCallback(cb) - iq.handled = True + return d def _onPrefsGetRequest(self, iq): """ @@ -517,16 +522,14 @@ This immediately replies with a result response. """ - response = toResponse(iq, 'result') - + iq.handled = True requestor = jid.JID(iq['from']) def cb(prefs): - response.addChild(prefs.toElement()) - self.xmlstream.send(response) + return prefs.toElement() - self.resource.onPrefsGetRequest(requestor).addCallback(cb) - iq.handled = True + d = self.resource.onPrefsGetRequest(requestor).addCallback(cb) + return d def _onPrefsSetRequest(self, iq): """ @@ -534,17 +537,16 @@ This immediately replies with a result response. """ - response = toResponse(iq, 'result') + iq.handled = True - prefs = MAMPrefs.parse(iq.prefs) + prefs = MAMPrefs.fromElement(iq.prefs) requestor = jid.JID(iq['from']) def cb(prefs): - response.addChild(prefs.toElement()) - self.xmlstream.send(response) + return prefs.toElement() - self.resource.onPrefsSetRequest(prefs, requestor).addCallback(cb) - iq.handled = True + d = self.resource.onPrefsSetRequest(prefs, requestor).addCallback(cb) + return d def getDiscoInfo(self, requestor, target, nodeIdentifier=''): if nodeIdentifier: @@ -555,25 +557,22 @@ return [] -def datetime2utc(datetime): +def datetime2utc(datetime_obj): """Convert a datetime to a XEP-0082 compliant UTC datetime. - @param datetime: Offset-aware timestamp to convert. - @type datetime: L{datetime<datetime.datetime>} + @param datetime_obj: Offset-aware timestamp to convert. + @type datetime_obj: L{datetime<datetime.datetime>} @return: The datetime converted to UTC. @rtype: C{unicode} """ stampFormat = '%Y-%m-%dT%H:%M:%SZ' - return datetime.astimezone(tzutc()).strftime(stampFormat) + return datetime_obj.astimezone(tz.tzutc()).strftime(stampFormat) -def buildForm(formType='submit', start=None, end=None, with_jid=None, extra_fields=None): +def buildForm(start=None, end=None, with_jid=None, extra_fields=None, formType='submit'): """Prepare a Data Form for MAM. - @param formType: The type of the Data Form ('submit' or 'form'). - @type formType: C{unicode} - @param start: Offset-aware timestamp to filter out older messages. @type start: L{datetime<datetime.datetime>} @@ -587,6 +586,9 @@ specification. @type: C{list} + @param formType: The type of the Data Form ('submit' or 'form'). + @type formType: C{unicode} + @return: XEP-0004 Data Form object. @rtype: L{Form<wokkel.data_form.Form>} """