class Subscription(VElement): elementName = 'subscription' node = fields.StringAttr('node', required=False) jid = fields.JidAttr('jid') subscription = fields.JidAttr('subscription') subid = fields.StringAttr('subid', required=False)
class SIRequest(SIElement): result_class = SIResponse # TODO: error_class mime_type = fields.StringAttr('mime-type', required=False) id_ = fields.StringAttr('id') profile = fields.StringAttr('profile') def make_reply(self, si): meta = {} streams = si.streams allowed = None for method in self.feature.methods.stream_method.options: if method.value in streams.keys(): allowed = method.value break if not allowed: # TODO: application specific error raise errors.BadRequestException reply_form = self.feature.methods.make_submit_form() reply_form.stream_method.value = allowed feature = Feature(methods=reply_form) reply = self.result_class(feature=feature, parent=self.iq.makeResult()) si.receive(allowed, self.id_, self.iq.from_, meta) sid = self.id_ def canceller(_): stream = si.streams[allowed] stream.unregisterSession(sid=sid) deferred = defer.Deferred(canceller) return reply, deferred, meta
class FileRequest(File): name_ = fields.StringAttr('name') size = fields.IntegerAttr('size') hash_ = fields.StringAttr('hash', required=False) date = fields.DateTimeAttr('date', required=False) description = fields.StringNode('desc', required=False) range_ = fields.ElementNode(Range, required=False)
class Items(VElement): elementName = 'items' items = fields.ElementNode(Item, listed=True, required=False) node = fields.StringAttr('node') max_items = fields.StringAttr('max_items', required=False) retracts = fields.ElementNode(Retract, listed=True, required=False)
class Iq(Stanza): """ Stanza-inheritor class that implements an answer-query type mechanism. """ elementName = 'iq' type_ = fields.StringAttr('type') id = fields.StringAttr('id') def __init__(self, **kwargs): """ Constructor controls for valid value of id, calls superclass-constructor and sets deferred attribute for set/get - type queries """ if 'id' not in kwargs: kwargs['id'] = uuid.uuid4() super(Iq, self).__init__(**kwargs) if not self.type_ in ('result', 'error') and \ not kwargs.get('dont_defer', False): self.deferred = Deferred() def clean_type_(self, value): """ Filters stanzas by type. Used for validation of type\_ field. Raises exception for invalid stanzas. :raises: ElementParseError """ if value not in ('set', 'get', 'result', 'error'): raise ElementParseError, 'Wrong Iq Type %s' % value return value def clean_id(self, value): """ Set correct value if id was not found. Used for validation of id. """ if value is None: self.addUniqueId() return self.id return value def makeResult(self): """Returns result-type Iq stanza""" return Iq(to=self.from_, from_=self.to, id=self.id, type_='result', uri=self.uri)
class StreamHost(VElement): elementName = 'streamhost' rhost = fields.StringAttr('host') jid = fields.JidAttr('jid') port = fields.StringAttr('port') def clean_port(self, value): try: value = int(value) assert value < 65536 except (ValueError, TypeError, AssertionError): raise NotAcceptableException return value
class DataElement(VElement): elementName = 'data' elementUri = IBB_NS seq = fields.StringAttr('seq') sid = fields.StringAttr('sid') def clean_seq(self, value): try: value = int(value) except (ValueError, TypeError, AssertionError): raise errors.BadRequestException value = value % 65536 return value
class UserItemInfo(VElement): """ VElement-inheritor class for user info from xml stanzas. Used in multi-user chat presences and also in administrator's queries. """ elementName = 'item' elementUri = 'http://jabber.org/protocol/muc#user' affiliation = fields.StringAttr('affiliation', required=False) role = fields.StringAttr('role', required=False) nick = fields.StringAttr('nick', required=False) jid = fields.JidAttr('jid', required=False) reason = fields.StringNode('reason', required=False)
class Subscriptions(VElement): elementName = 'subscriptions' node = fields.StringAttr('node', required=False) subscriptions = fields.ElementNode(Subscription, listed=True, required=False)
class Error(VElement): """ Extends VElement from twilix.base. Contains fields corresponding to the rfc 3920. Attributes: type\_ -- string attribute 'type' condition -- condition node with Condition text -- string node 'text' """ elementName = 'error' type_ = fields.StringAttr('type') condition = ConditionNode(Condition) text = fields.StringNode('text', required=False, uri='urn:ietf:params:xml:ns:xmpp-stanzas') def clean_type_(self, value): """ Cut off errors with wrong type. Used for validaion. :returns: value if it has correct type. :raises: ElementParseError if value has a wrong type. """ if value not in ('cancel', 'continue', 'modify', 'auth', 'wait'): raise ElementParseError, 'Wrong Error Type %s' % value return value
class Field(VElement): elementName = 'field' fieldType = None # To be redefined in derived classes type_ = fields.StringAttr('type') label = fields.StringAttr('label', required=False) var = fields.StringAttr('var') required = fields.FlagNode('required', default=False, required=False) values = fields.StringNode('value', listed=True, required=False) options = fields.ElementNode(Option, listed=True, required=False) def __init__(self, value=None, *args, **kwargs): self.kwargs = kwargs super(Field, self).__init__(*args, **kwargs) self.type_ = self.fieldType self.value = value def prepare_to_submit(self): self.label = None self.options = () self.required = None def restore_for_validation(self, kwargs): self.options = kwargs.get('options') def clean_type_(self, value): if value == self.fieldType: return value raise WrongElement def fclean(self, values): if self.kwargs.get('required') and not values: raise ElementParseError, "Form field %s %s is required" % \ (self.var, self.type_) return values def _get_value(self): return self.values def _set_value(self, value): self.values = value def _del_value(self, value): self.values = () value = property(_get_value, _set_value, _del_value)
class HtmlHead(VElement): """ Extends VElement class from twilix.base Realize head tag of html. """ elementName = 'head' profile = fields.StringAttr('profile', required=False) title = fields.StringNode('title')
class RosterItem(VElement): """ Class for xml roster item node. Inheritor of VElement. Attributes: jid -- jid attribute 'jid' subscription -- string attribute 'subscription' ask -- string attribute 'ask' nick -- string attribute 'name' groups -- string attribute 'group' """ elementName = 'item' elementUri = 'jabber:iq:roster' jid = fields.JidAttr('jid') subscription = fields.StringAttr('subscription', required=False) ask = fields.StringAttr('ask', required=False) nick = fields.StringAttr('name', required=False) groups = fields.StringNode('group', required=False, listed=True, unique=True) def __init__(self, **kwargs): super(RosterItem, self).__init__(**kwargs) self.presences = {} def is_online(self): return bool(self.presences) def __unicode__(self): """Unicode converter.""" return '<RosterItem %s %s, subscription %s>' % \ (self.jid, self.nick, self.subscription) def __repr__(self): return self.__unicode__()
class DiscoItem(VElement): """ Extends VElement. Describe base discovery item element with fields that corresponds to the protocol. Attributes: jid -- jid attribute iname -- string attribute contains human readable name node -- string attribute """ elementName = 'item' jid = fields.JidAttr('jid') iname = fields.StringAttr('name', required=False) node = fields.StringAttr('node', required=False)
class OpenQuery(IbbQuery): elementName = 'open' block_size = fields.StringAttr('block-size') sid = fields.StringAttr('sid') stanza_type = fields.StringAttr('stanza', required=False) def clean_block_size(self, value): try: value = int(value) assert value <= 65535 except (ValueError, TypeError, AssertionError): raise errors.BadRequestException return value def clean_stanza(self, value): if value not in ('iq', 'message'): raise errors.BadRequestException return value
class CapsElement(VElement): #XXX: invate automatize caps calculation """ Extends VElement. Describe node for entity capabilities with fields that corresponds to the protocol. Attributes: hash\_ - string attribute contains features hash type node -- string attribute contains caps node ver -- string attribute contains features hash """ elementName = 'c' elementUri = 'http://jabber.org/protocol/caps' hash_ = fields.StringAttr('hash') node = fields.StringAttr('node') ver = fields.StringAttr('ver')
class Identity(VElement): """ Extends VElement. Describe node for identity with fields that corresponds to the protocol. Attributes: category -- string attribute type\_ -- string attribute iname -- string attribute contains human readable name See: http://xmpp.org/registrar/disco-categories.html """ elementName = 'identity' category = fields.StringAttr('category') type_ = fields.StringAttr('type') iname = fields.StringAttr('name', required=False)
class Feature(VElement): """ Extends VElement. Describes node for feature with field that corresponds to the protocol. Attributes: var -- string attribute with a feature namespace """ elementName = 'feature' var = fields.StringAttr('var')
class Query(VElement): """ VElement-inheritor class. Base for other query-type classes. There is a query part of query-answer mechanism. Contains fields corresponding to the protocol. Attributes: node -- string attribute 'node' """ elementName = 'query' parentClass = Iq node = fields.StringAttr('node', required=False)
class Form(VElement): elementName = 'x' elementUri = 'jabber:x:data' type_ = f.StringAttr('type') title = f.StringNode('title', required=False) instructions = f.StringNode('instructions', required=False) def clean_type_(self, value): if value not in ('form', 'submit', 'cancel', 'result'): raise ElementParseError return value def clean(self): if self.type_ != 'submit': return for fname in self.fields: field = getattr(self, fname) value = field.fclean(field.value) field.value = value @property def fields(self): fields = [] for name, attr in self.nodesProps.items(): if isinstance(attr, FormField): fields.append(name) return fields def make_submit_form(self): assert self.type_ == 'form' sform = copy.deepcopy(self) fields = sform.fields for name in fields: field = getattr(sform, name) field.prepare_to_submit() setattr(sform, name, field) sform.type_ = 'submit' sform.title = None sform.instructions = None return sform def make_cancel_form(self): assert self.type_ == 'form' sform = copy.deepcopy(self) sform.children = [] sform.type_ = 'cancel' return sform
class DiscoInfoQuery(Query): """ Extends Query class. Contains information about features and identities. """ elementUri = 'http://jabber.org/protocol/disco#info' identities = fields.ElementNode(Identity, required=False, listed=True, unique=True) features = fields.ElementNode(Feature, required=False, listed=True, unique=True) node = fields.StringAttr('node', required=False)
class DiscoItemsQuery(Query): """ Extends Query class. Attibutes: items -- element node with DiscoItem node -- string attribute 'node' """ elementUri = 'http://jabber.org/protocol/disco#items' items = fields.ElementNode(DiscoItem, required=False, listed=True, unique=True) node = fields.StringAttr('node', required=False)
class Stanza(VElement): """ Extends VElement class from twilix.base. Contains fields corresponding to the protocol. Base class for all base XMPP Stanzas (iq, message, presence). Attributes: to -- jid attribue 'to' from\_ -- jid attribue 'from' type -- string attribue 'type' id -- string attribue 'id' lang -- string attribue 'xml:lang' """ elementUri = ( 'jabber:client', 'jabber:server', 'jabber:component:accept', None, ) to = fields.JidAttr('to', required=False) from_ = fields.JidAttr('from', required=False) type_ = fields.StringAttr('type', required=False) id = fields.StringAttr('id', required=False) lang = fields.StringAttr('xml:lang', required=False) def __init__(self, *args, **kwargs): """ Makes a superclass intialization and set attributes for deferred-style stanzas. """ super(Stanza, self).__init__(*args, **kwargs) if 'result_class' in kwargs: self._result_class = kwargs['result_class'] if 'error_class' in kwargs: self._error_class = kwargs['error_class'] def __unicode__(self): """Overrrides unicode converter.""" return self.toXml() def __repr__(self): """Makes avaliable to show stanza in xml format.""" return self.toXml() def makeError(self, content): """ Creates ErrorStanza from self and then returns it. Used to make Error Stanza as reply on any Stanza. :param content: Error element. :returns: Error Stanza with Error element. """ res = ErrorStanza(to=self.from_, from_=self.to, type_='error', el_name=self.elementName, id=self.id, error=content) res.children = self.children + res.children return res def get_reply(self): """ Creates the reply stanza. There is REPLY_CLASS (if defined) or self-type stanza's class. """ cls = getattr(self, 'REPLY_CLASS', None) or self.__class__ return cls(to=self.from_, from_=self.to, type_=self.type_, id=self.id)
class DeleteNodeElement(VElement): elementName = 'delete' node = fields.StringAttr('node')
class ConfigureNode(VElement): elementName = 'configure' node = fields.StringAttr('node')
class Retract(VElement): elementName = 'retract' node = fields.StringAttr('node') items = fields.ElementNode(Item, listed=True)
class CreateNodeElement(VElement): elementName = 'create' node = fields.StringAttr('node')
class SubscribeElement(VElement): elementName = 'subscribe' node = fields.StringAttr('node') jid = fields.JidAttr('jid')
class Retract(VElement): elementName = 'retract' id_ = fields.StringAttr('id', required=True)
class UnsubscribeElement(SubscribeElement): elementName = 'unsubscribe' subid = fields.StringAttr('subid', required=False)