pyxmpp.jabber.muccore

Package pyxmpp :: Package jabber :: Module muccore |
[hide private]

# Source Code for Module pyxmpp.jabber.muccore

      1  #
      2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net>
      3  #
      4  # This program is free software; you can redistribute it and/or modify
      5  # it under the terms of the GNU Lesser General Public License Version
      6  # 2.1 as published by the Free Software Foundation.
      7  #
      8  # This program is distributed in the hope that it will be useful,
      9  # but WITHOUT ANY WARRANTY; without even the implied warranty of
     10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     11  # GNU Lesser General Public License for more details.
     12  #
     13  # You should have received a copy of the GNU Lesser General Public
     14  # License along with this program; if not, write to the Free Software
     15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
     16  #
     17  """Jabber Multi-User Chat implementation.
     18
     19  Normative reference:
     20    - `JEP 45 <http://www.jabber.org/jeps/jep-0045.html>`__
     21  """
     22
     23  __revision__="$Id: muccore.py 714 2010-04-05 10:20:10Z jajcus $"
     24  __docformat__="restructuredtext en"
     25
     26  import libxml2
     27
     28  from pyxmpp.utils import to_utf8,from_utf8
     29  from pyxmpp.xmlextra import common_doc, common_root, common_ns, get_node_ns_uri
     30  from pyxmpp.presence import Presence
     31  from pyxmpp.iq import Iq
     32  from pyxmpp.jid import JID
     33  from pyxmpp import xmlextra
     34  from pyxmpp.objects import StanzaPayloadWrapperObject
     35  from pyxmpp.xmlextra import xml_element_iter
     36
     37  MUC_NS="http://jabber.org/protocol/muc"
     38  MUC_USER_NS=MUC_NS+"#user"
     39  MUC_ADMIN_NS=MUC_NS+"#admin"
     40  MUC_OWNER_NS=MUC_NS+"#owner"
     41
     42  affiliations=("admin","member","none","outcast","owner")
     43  roles=("moderator","none","participant","visitor")
     44
     45 -class MucXBase(StanzaPayloadWrapperObject):
     46      """
     47      Base class for MUC-specific stanza payload - wrapper around
     48      an XML element.
     49
     50      :Ivariables:
     51          - `xmlnode`: the wrapped XML node
     52      """
     53      element="x"
     54      ns=None
     55 -    def __init__(self, xmlnode=None, copy=True, parent=None):
     56          """
     57          Copy MucXBase object or create a new one, possibly
     58          based on or wrapping an XML node.
     59
     60          :Parameters:
     61              - `xmlnode`: is the object to copy or an XML node to wrap.
     62              - `copy`: when `True` a copy of the XML node provided will be included
     63                in `self`, the node will be copied otherwise.
     64              - `parent`: parent node for the created/copied XML element.
     65          :Types:
     66              - `xmlnode`: `MucXBase` or `libxml2.xmlNode`
     67              - `copy`: `bool`
     68              - `parent`: `libxml2.xmlNode`
     69          """
     70          if self.ns==None:
     71              raise RuntimeError,"Pure virtual class called"
     72          self.xmlnode=None
     73          self.borrowed=False
     74          if isinstance(xmlnode,libxml2.xmlNode):
     75              if copy:
     76                  self.xmlnode=xmlnode.docCopyNode(common_doc,1)
     77                  common_root.addChild(self.xmlnode)
     78              else:
     79                  self.xmlnode=xmlnode
     80                  self.borrowed=True
     81              if copy:
     82                  ns=xmlnode.ns()
     83                  xmlextra.replace_ns(self.xmlnode, ns, common_ns)
     84          elif isinstance(xmlnode,MucXBase):
     85              if not copy:
     86                  raise TypeError, "MucXBase may only be copied"
     87              self.xmlnode=xmlnode.xmlnode.docCopyNode(common_doc,1)
     88              common_root.addChild(self.xmlnode)
     89          elif xmlnode is not None:
     90              raise TypeError, "Bad MucX constructor argument"
     91          else:
     92              if parent:
     93                  self.xmlnode=parent.newChild(None,self.element,None)
     94                  self.borrowed=True
     95              else:
     96                  self.xmlnode=common_root.newChild(None,self.element,None)
     97              ns=self.xmlnode.newNs(self.ns,None)
     98              self.xmlnode.setNs(ns)
     99
    100 -    def __del__(self):
    101          if self.xmlnode:
    102              self.free()
    103
    104 -    def free(self):
    105          """
    106          Unlink and free the XML node owned by `self`.
    107          """
    108          if not self.borrowed:
    109              self.xmlnode.unlinkNode()
    110              self.xmlnode.freeNode()
    111          self.xmlnode=None
    112
    113 -    def free_borrowed(self):
    114          """
    115          Detach the XML node borrowed by `self`.
    116          """
    117          self.xmlnode=None
    118
    119 -    def xpath_eval(self,expr):
    120          """
    121          Evaluate XPath expression in context of `self.xmlnode`.
    122
    123          :Parameters:
    124              - `expr`: the XPath expression
    125          :Types:
    126              - `expr`: `unicode`
    127
    128          :return: the result of the expression evaluation.
    129          :returntype: list of `libxml2.xmlNode`
    130          """
    131          ctxt = common_doc.xpathNewContext()
    132          ctxt.setContextNode(self.xmlnode)
    133          ctxt.xpathRegisterNs("muc",self.ns.getContent())
    134          ret=ctxt.xpathEval(to_utf8(expr))
    135          ctxt.xpathFreeContext()
    136          return ret
    137
    138 -    def serialize(self):
    139          """
    140          Serialize `self` as XML.
    141
    142          :return: serialized `self.xmlnode`.
    143          :returntype: `str`
    144          """
    145          return self.xmlnode.serialize()
    146
    147 -class MucX(MucXBase):
    148      """
    149      Wrapper for http://www.jabber.org/protocol/muc namespaced
    150      stanza payload "x" elements.
    151      """
    152      ns=MUC_NS
    153 -    def __init__(self, xmlnode=None, copy=True, parent=None):
    154          MucXBase.__init__(self,xmlnode=xmlnode, copy=copy, parent=parent)
    155
    156 -    def set_history(self, parameters):
    157          """
    158          Set history parameters.
    159
    160          Types:
    161              - `parameters`: `HistoryParameters`
    162          """
    163          for child in xml_element_iter(self.xmlnode.children):
    164              if get_node_ns_uri(child) == MUC_NS and child.name == "history":
    165                  child.unlinkNode()
    166                  child.freeNode()
    167                  break
    168
    169          if parameters.maxchars and parameters.maxchars < 0:
    170              raise ValueError, "History parameter maxchars must be positive"
    171          if parameters.maxstanzas and parameters.maxstanzas < 0:
    172              raise ValueError, "History parameter maxstanzas must be positive"
    173          if parameters.maxseconds and parameters.maxseconds < 0:
    174              raise ValueError, "History parameter maxseconds must be positive"
    175
    176          hnode=self.xmlnode.newChild(self.xmlnode.ns(), "history", None)
    177
    178          if parameters.maxchars is not None:
    179              hnode.setProp("maxchars", str(parameters.maxchars))
    180          if parameters.maxstanzas is not None:
    181              hnode.setProp("maxstanzas", str(parameters.maxstanzas))
    182          if parameters.maxseconds is not None:
    183              hnode.setProp("maxseconds", str(parameters.maxseconds))
    184          if parameters.since is not None:
    185              hnode.setProp("since", parameters.since.strftime("%Y-%m-%dT%H:%M:%SZ"))
    186
    187 -    def get_history(self):
    188          """Return history parameters carried by the stanza.
    189
    190          :returntype: `HistoryParameters`"""
    191          for child in xml_element_iter(self.xmlnode.children):
    192              if get_node_ns_uri(child) == MUC_NS and child.name == "history":
    193                  maxchars = from_utf8(child.prop("maxchars"))
    194                  if maxchars is not None:
    195                      maxchars = int(maxchars)
    196                  maxstanzas = from_utf8(child.prop("maxstanzas"))
    197                  if maxstanzas is not None:
    198                      maxstanzas = int(maxstanzas)
    199                  maxseconds = from_utf8(child.prop("maxseconds"))
    200                  if maxseconds is not None:
    201                      maxseconds = int(maxseconds)
    202                  # TODO: since -- requires parsing of Jabber dateTime profile
    203                  since = None
    204                  return HistoryParameters(maxchars, maxstanzas, maxseconds, since)
    205
    206 -    def set_password(self, password):
    207          """Set password for the MUC request.
    208
    209          :Parameters:
    210              - `password`: password
    211          :Types:
    212              - `password`: `unicode`"""
    213          for child in xml_element_iter(self.xmlnode.children):
    214              if get_node_ns_uri(child) == MUC_NS and child.name == "password":
    215                  child.unlinkNode()
    216                  child.freeNode()
    217                  break
    218
    219          if password is not None:
    220              self.xmlnode.newTextChild(self.xmlnode.ns(), "password", to_utf8(password))
    221
    222 -    def get_password(self):
    223          """Get password from the MUC request.
    224
    225          :returntype: `unicode`
    226          """
    227          for child in xml_element_iter(self.xmlnode.children):
    228              if get_node_ns_uri(child) == MUC_NS and child.name == "password":
    229                  return from_utf8(child.getContent())
    230          return None
    231
    232 -class HistoryParameters(object):
    233      """Provides parameters for MUC history management
    234
    235      :Ivariables:
    236          - `maxchars`: limit of the total number of characters in history.
    237          - `maxstanzas`: limit of the total number of messages in history.
    238          - `seconds`: send only messages received in the last `seconds` seconds.
    239          - `since`: Send only the messages received since the dateTime (UTC)
    240            specified.
    241      :Types:
    242          - `maxchars`: `int`
    243          - `maxstanzas`: `int`
    244          - `seconds`: `int`
    245          - `since`: `datetime.datetime`
    246      """
    247 -    def __init__(self, maxchars = None, maxstanzas = None, maxseconds = None, since = None):
    248          """Initializes a `HistoryParameters` object.
    249
    250          :Parameters:
    251              - `maxchars`: limit of the total number of characters in history.
    252              - `maxstanzas`: limit of the total number of messages in history.
    253              - `maxseconds`: send only messages received in the last `seconds` seconds.
    254              - `since`: Send only the messages received since the dateTime specified.
    255          :Types:
    256              - `maxchars`: `int`
    257              - `maxstanzas`: `int`
    258              - `maxseconds`: `int`
    259              - `since`: `datetime.datetime`
    260          """
    261          self.maxchars = maxchars
    262          self.maxstanzas = maxstanzas
    263          self.maxseconds = maxseconds
    264          self.since = since
    265
    266
    267 -class MucItemBase(object):
    268      """
    269      Base class for <status/> and <item/> element wrappers.
    270      """
    271 -    def __init__(self):
    272          if self.__class__ is MucItemBase:
    273              raise RuntimeError,"Abstract class called"
    274
    275 -class MucItem(MucItemBase):
    276      """
    277      MUC <item/> element -- describes a room occupant.
    278
    279      :Ivariables:
    280          - `affiliation`: affiliation of the user.
    281          - `role`: role of the user.
    282          - `jid`: JID of the user.
    283          - `nick`: nickname of the user.
    284          - `actor`: actor modyfying the user data.
    285          - `reason`: reason of change of the user data.
    286      :Types:
    287          - `affiliation`: `str`
    288          - `role`: `str`
    289          - `jid`: `JID`
    290          - `nick`: `unicode`
    291          - `actor`: `JID`
    292          - `reason`: `unicode`
    293      """
    294 -    def __init__(self,xmlnode_or_affiliation,role=None,jid=None,nick=None,actor=None,reason=None):
    295          """
    296          Initialize a `MucItem` object.
    297
    298          :Parameters:
    299              - `xmlnode_or_affiliation`: XML node to be pased or the affiliation of
    300                the user being described.
    301              - `role`: role of the user.
    302              - `jid`: JID of the user.
    303              - `nick`: nickname of the user.
    304              - `actor`: actor modyfying the user data.
    305              - `reason`: reason of change of the user data.
    306          :Types:
    307              - `xmlnode_or_affiliation`: `libxml2.xmlNode` or `str`
    308              - `role`: `str`
    309              - `jid`: `JID`
    310              - `nick`: `unicode`
    311              - `actor`: `JID`
    312              - `reason`: `unicode`
    313          """
    314          self.jid,self.nick,self.actor,self.affiliation,self.reason,self.role=(None,)*6
    315          MucItemBase.__init__(self)
    316          if isinstance(xmlnode_or_affiliation,libxml2.xmlNode):
    317              self.__from_xmlnode(xmlnode_or_affiliation)
    318          else:
    319              self.__init(xmlnode_or_affiliation,role,jid,nick,actor,reason)
    320
    321 -    def __init(self,affiliation,role,jid=None,nick=None,actor=None,reason=None):
    322          """Initialize a `MucItem` object from a set of attributes.
    323
    324          :Parameters:
    325              - `affiliation`: affiliation of the user.
    326              - `role`: role of the user.
    327              - `jid`: JID of the user.
    328              - `nick`: nickname of the user.
    329              - `actor`: actor modyfying the user data.
    330              - `reason`: reason of change of the user data.
    331          :Types:
    332              - `affiliation`: `str`
    333              - `role`: `str`
    334              - `jid`: `JID`
    335              - `nick`: `unicode`
    336              - `actor`: `JID`
    337              - `reason`: `unicode`
    338          """
    339          if not affiliation:
    340              affiliation=None
    341          elif affiliation not in affiliations:
    342              raise ValueError,"Bad affiliation"
    343          self.affiliation=affiliation
    344          if not role:
    345              role=None
    346          elif role not in roles:
    347              raise ValueError,"Bad role"
    348          self.role=role
    349          if jid:
    350              self.jid=JID(jid)
    351          else:
    352              self.jid=None
    353          if actor:
    354              self.actor=JID(actor)
    355          else:
    356              self.actor=None
    357          self.nick=nick
    358          self.reason=reason
    359
    360 -    def __from_xmlnode(self, xmlnode):
    361          """Initialize a `MucItem` object from an XML node.
    362
    363          :Parameters:
    364              - `xmlnode`: the XML node.
    365          :Types:
    366              - `xmlnode`: `libxml2.xmlNode`
    367          """
    368          actor=None
    369          reason=None
    370          n=xmlnode.children
    371          while n:
    372              ns=n.ns()
    373              if ns and ns.getContent()!=MUC_USER_NS:
    374                  continue
    375              if n.name=="actor":
    376                  actor=n.getContent()
    377              if n.name=="reason":
    378                  reason=n.getContent()
    379              n=n.next
    380          self.__init(
    381              from_utf8(xmlnode.prop("affiliation")),
    382              from_utf8(xmlnode.prop("role")),
    383              from_utf8(xmlnode.prop("jid")),
    384              from_utf8(xmlnode.prop("nick")),
    385              from_utf8(actor),
    386              from_utf8(reason),
    387              );
    388
    389 -    def as_xml(self,parent):
    390          """
    391          Create XML representation of `self`.
    392
    393          :Parameters:
    394              - `parent`: the element to which the created node should be linked to.
    395          :Types:
    396              - `parent`: `libxml2.xmlNode`
    397
    398          :return: an XML node.
    399          :returntype: `libxml2.xmlNode`
    400          """
    401          n=parent.newChild(None,"item",None)
    402          if self.actor:
    403              n.newTextChild(None,"actor",to_utf8(self.actor))
    404          if self.reason:
    405              n.newTextChild(None,"reason",to_utf8(self.reason))
    406          n.setProp("affiliation",to_utf8(self.affiliation))
    407          if self.role:
    408              n.setProp("role",to_utf8(self.role))
    409          if self.jid:
    410              n.setProp("jid",to_utf8(self.jid.as_unicode()))
    411          if self.nick:
    412              n.setProp("nick",to_utf8(self.nick))
    413          return n
    414
    415 -class MucStatus(MucItemBase):
    416      """
    417      MUC <item/> element - describes special meaning of a stanza
    418
    419      :Ivariables:
    420          - `code`: staus code, as defined in JEP 45
    421      :Types:
    422          - `code`: `int`
    423      """
    424 -    def __init__(self,xmlnode_or_code):
    425          """Initialize a `MucStatus` element.
    426
    427          :Parameters:
    428              - `xmlnode_or_code`: XML node to parse or a status code.
    429          :Types:
    430              - `xmlnode_or_code`: `libxml2.xmlNode` or `int`
    431          """
    432          self.code=None
    433          MucItemBase.__init__(self)
    434          if isinstance(xmlnode_or_code,libxml2.xmlNode):
    435              self.__from_xmlnode(xmlnode_or_code)
    436          else:
    437              self.__init(xmlnode_or_code)
    438
    439 -    def __init(self,code):
    440          """Initialize a `MucStatus` element from a status code.
    441
    442          :Parameters:
    443              - `code`: the status code.
    444          :Types:
    445              - `code`: `int`
    446          """
    447          code=int(code)
    448          if code<0 or code>999:
    449              raise ValueError,"Bad status code"
    450          self.code=code
    451
    452 -    def __from_xmlnode(self, xmlnode):
    453          """Initialize a `MucStatus` element from an XML node.
    454
    455          :Parameters:
    456              - `xmlnode`: XML node to parse.
    457          :Types:
    458              - `xmlnode`: `libxml2.xmlNode`
    459          """
    460          self.code=int(xmlnode.prop("code"))
    461
    462 -    def as_xml(self,parent):
    463          """
    464          Create XML representation of `self`.
    465
    466          :Parameters:
    467              - `parent`: the element to which the created node should be linked to.
    468          :Types:
    469              - `parent`: `libxml2.xmlNode`
    470
    471          :return: an XML node.
    472          :returntype: `libxml2.xmlNode`
    473          """
    474          n=parent.newChild(None,"status",None)
    475          n.setProp("code","%03i" % (self.code,))
    476          return n
    477
    478 -class MucUserX(MucXBase):
    479      """
    480      Wrapper for http://www.jabber.org/protocol/muc#user namespaced
    481      stanza payload "x" elements and usually containing information
    482      about a room user.
    483
    484      :Ivariables:
    485          - `xmlnode`: wrapped XML node
    486      :Types:
    487          - `xmlnode`: `libxml2.xmlNode`
    488      """
    489      ns=MUC_USER_NS
    490 -    def get_items(self):
    491          """Get a list of objects describing the content of `self`.
    492
    493          :return: the list of objects.
    494          :returntype: `list` of `MucItemBase` (`MucItem` and/or `MucStatus`)
    495          """
    496          if not self.xmlnode.children:
    497              return []
    498          ret=[]
    499          n=self.xmlnode.children
    500          while n:
    501              ns=n.ns()
    502              if ns and ns.getContent()!=self.ns:
    503                  pass
    504              elif n.name=="item":
    505                  ret.append(MucItem(n))
    506              elif n.name=="status":
    507                  ret.append(MucStatus(n))
    508              # FIXME: alt,decline,invite,password
    509              n=n.next
    510          return ret
    511 -    def clear(self):
    512          """
    513          Clear the content of `self.xmlnode` removing all <item/>, <status/>, etc.
    514          """
    515          if not self.xmlnode.children:
    516              return
    517          n=self.xmlnode.children
    518          while n:
    519              ns=n.ns()
    520              if ns and ns.getContent()!=MUC_USER_NS:
    521                  pass
    522              else:
    523                  n.unlinkNode()
    524                  n.freeNode()
    525              n=n.next
    526 -    def add_item(self,item):
    527          """Add an item to `self`.
    528
    529          :Parameters:
    530              - `item`: the item to add.
    531          :Types:
    532              - `item`: `MucItemBase`
    533          """
    534          if not isinstance(item,MucItemBase):
    535              raise TypeError,"Bad item type for muc#user"
    536          item.as_xml(self.xmlnode)
    537
    538 -class MucOwnerX(MucXBase):
    539      """
    540      Wrapper for http://www.jabber.org/protocol/muc#owner namespaced
    541      stanza payload "x" elements and usually containing information
    542      about a room user.
    543
    544      :Ivariables:
    545          - `xmlnode`: wrapped XML node.
    546      :Types:
    547          - `xmlnode`: `libxml2.xmlNode`
    548      """
    549      # FIXME: implement
    550      pass
    551
    552 -class MucAdminQuery(MucUserX):
    553      """
    554      Wrapper for http://www.jabber.org/protocol/muc#admin namespaced
    555      IQ stanza payload "query" elements and usually describing
    556      administrative actions or their results.
    557
    558      Not implemented yet.
    559      """
    560      ns=MUC_ADMIN_NS
    561      element="query"
    562
    563 -class MucStanzaExt:
    564      """
    565      Base class for MUC specific stanza extensions. Used together
    566      with one of stanza classes (Iq, Message or Presence).
    567      """
    568 -    def __init__(self):
    569          """Initialize a `MucStanzaExt` derived object."""
    570          if self.__class__ is MucStanzaExt:
    571              raise RuntimeError,"Abstract class called"
    572          self.xmlnode=None
    573          self.muc_child=None
    574
    575 -    def get_muc_child(self):
    576          """
    577          Get the MUC specific payload element.
    578
    579          :return: the object describing the stanza payload in MUC namespace.
    580          :returntype: `MucX` or `MucUserX` or `MucAdminQuery` or `MucOwnerX`
    581          """
    582          if self.muc_child:
    583              return self.muc_child
    584          if not self.xmlnode.children:
    585              return None
    586          n=self.xmlnode.children
    587          while n:
    588              if n.name not in ("x","query"):
    589                  n=n.next
    590                  continue
    591              ns=n.ns()
    592              if not ns:
    593                  n=n.next
    594                  continue
    595              ns_uri=ns.getContent()
    596              if (n.name,ns_uri)==("x",MUC_NS):
    597                  self.muc_child=MucX(n)
    598                  return self.muc_child
    599              if (n.name,ns_uri)==("x",MUC_USER_NS):
    600                  self.muc_child=MucUserX(n)
    601                  return self.muc_child
    602              if (n.name,ns_uri)==("query",MUC_ADMIN_NS):
    603                  self.muc_child=MucAdminQuery(n)
    604                  return self.muc_child
    605              if (n.name,ns_uri)==("query",MUC_OWNER_NS):
    606                  self.muc_child=MucOwnerX(n)
    607                  return self.muc_child
    608              n=n.next
    609
    610 -    def clear_muc_child(self):
    611          """
    612          Remove the MUC specific stanza payload element.
    613          """
    614          if self.muc_child:
    615              self.muc_child.free_borrowed()
    616              self.muc_child=None
    617          if not self.xmlnode.children:
    618              return
    619          n=self.xmlnode.children
    620          while n:
    621              if n.name not in ("x","query"):
    622                  n=n.next
    623                  continue
    624              ns=n.ns()
    625              if not ns:
    626                  n=n.next
    627                  continue
    628              ns_uri=ns.getContent()
    629              if ns_uri in (MUC_NS,MUC_USER_NS,MUC_ADMIN_NS,MUC_OWNER_NS):
    630                  n.unlinkNode()
    631                  n.freeNode()
    632              n=n.next
    633
    634 -    def make_muc_userinfo(self):
    635          """
    636          Create <x xmlns="...muc#user"/> element in the stanza.
    637
    638          :return: the element created.
    639          :returntype: `MucUserX`
    640          """
    641          self.clear_muc_child()
    642          self.muc_child=MucUserX(parent=self.xmlnode)
    643          return self.muc_child
    644
    645 -    def make_muc_admin_quey(self):
    646          """
    647          Create <query xmlns="...muc#admin"/> element in the stanza.
    648
    649          :return: the element created.
    650          :returntype: `MucAdminQuery`
    651          """
    652          self.clear_muc_child()
    653          self.muc_child=MucAdminQuery(parent=self.xmlnode)
    654          return self.muc_child
    655
    656 -    def muc_free(self):
    657          """
    658          Free MUC specific data.
    659          """
    660          if self.muc_child:
    661              self.muc_child.free_borrowed()
    662
    663 -class MucPresence(Presence,MucStanzaExt):
    664      """
    665      Extend `Presence` with MUC related interface.
    666      """
    667 -    def __init__(self, xmlnode=None,from_jid=None,to_jid=None,stanza_type=None,stanza_id=None,
    668              show=None,status=None,priority=0,error=None,error_cond=None):
    669          """Initialize a `MucPresence` object.
    670
    671          :Parameters:
    672              - `xmlnode`: XML node to_jid be wrapped into the `MucPresence` object
    673                or other Presence object to be copied. If not given then new
    674                presence stanza is created using following parameters.
    675              - `from_jid`: sender JID.
    676              - `to_jid`: recipient JID.
    677              - `stanza_type`: staza type: one of: None, "available", "unavailable",
    678                "subscribe", "subscribed", "unsubscribe", "unsubscribed" or
    679                "error". "available" is automaticaly changed to_jid None.
    680              - `stanza_id`: stanza id -- value of stanza's "id" attribute
    681              - `show`: "show" field of presence stanza. One of: None, "away",
    682                "xa", "dnd", "chat".
    683              - `status`: descriptive text for the presence stanza.
    684              - `priority`: presence priority.
    685              - `error_cond`: error condition name. Ignored if `stanza_type` is not "error"
    686          :Types:
    687              - `xmlnode`: `unicode` or `libxml2.xmlNode` or `pyxmpp.stanza.Stanza`
    688              - `from_jid`: `JID`
    689              - `to_jid`: `JID`
    690              - `stanza_type`: `unicode`
    691              - `stanza_id`: `unicode`
    692              - `show`: `unicode`
    693              - `status`: `unicode`
    694              - `priority`: `unicode`
    695              - `error_cond`: `unicode`"""
    696          MucStanzaExt.__init__(self)
    697          Presence.__init__(self,xmlnode,from_jid=from_jid,to_jid=to_jid,
    698                  stanza_type=stanza_type,stanza_id=stanza_id,
    699                  show=show,status=status,priority=priority,
    700                  error=error,error_cond=error_cond)
    701
    702 -    def copy(self):
    703          """
    704          Return a copy of `self`.
    705          """
    706          return MucPresence(self)
    707
    708 -    def make_join_request(self, password = None, history_maxchars = None,
    709              history_maxstanzas = None, history_seconds = None,
    710              history_since = None):
    711          """
    712          Make the presence stanza a MUC room join request.
    713
    714          :Parameters:
    715              - `password`: password to the room.
    716              - `history_maxchars`: limit of the total number of characters in
    717                history.
    718              - `history_maxstanzas`: limit of the total number of messages in
    719                history.
    720              - `history_seconds`: send only messages received in the last
    721                `seconds` seconds.
    722              - `history_since`: Send only the messages received since the
    723                dateTime specified (UTC).
    724          :Types:
    725              - `password`: `unicode`
    726              - `history_maxchars`: `int`
    727              - `history_maxstanzas`: `int`
    728              - `history_seconds`: `int`
    729              - `history_since`: `datetime.datetime`
    730          """
    731          self.clear_muc_child()
    732          self.muc_child=MucX(parent=self.xmlnode)
    733          if (history_maxchars is not None or history_maxstanzas is not None
    734                  or history_seconds is not None or history_since is not None):
    735              history = HistoryParameters(history_maxchars, history_maxstanzas,
    736                      history_seconds, history_since)
    737              self.muc_child.set_history(history)
    738          if password is not None:
    739              self.muc_child.set_password(password)
    740
    741 -    def get_join_info(self):
    742          """If `self` is a MUC room join request return the information contained.
    743
    744          :return: the join request details or `None`.
    745          :returntype: `MucX`
    746          """
    747          x=self.get_muc_child()
    748          if not x:
    749              return None
    750          if not isinstance(x,MucX):
    751              return None
    752          return x
    753
    754 -    def free(self):
    755          """Free the data associated with this `MucPresence` object."""
    756          self.muc_free()
    757          Presence.free(self)
    758
    759 -class MucIq(Iq,MucStanzaExt):
    760      """
    761      Extend `Iq` with MUC related interface.
    762      """
    763 -    def __init__(self,xmlnode=None,from_jid=None,to_jid=None,stanza_type=None,stanza_id=None,
    764              error=None,error_cond=None):
    765          """Initialize an `Iq` object.
    766
    767          :Parameters:
    768              - `xmlnode`: XML node to_jid be wrapped into the `Iq` object
    769                or other Iq object to be copied. If not given then new
    770                presence stanza is created using following parameters.
    771              - `from_jid`: sender JID.
    772              - `to_jid`: recipient JID.
    773              - `stanza_type`: staza type: one of: "get", "set", "result" or "error".
    774              - `stanza_id`: stanza id -- value of stanza's "id" attribute. If not
    775                given, then unique for the session value is generated.
    776              - `error_cond`: error condition name. Ignored if `stanza_type` is not "error".
    777          :Types:
    778              - `xmlnode`: `unicode` or `libxml2.xmlNode` or `Iq`
    779              - `from_jid`: `JID`
    780              - `to_jid`: `JID`
    781              - `stanza_type`: `unicode`
    782              - `stanza_id`: `unicode`
    783              - `error_cond`: `unicode`"""
    784          MucStanzaExt.__init__(self)
    785          Iq.__init__(self,xmlnode,from_jid=from_jid,to_jid=to_jid,
    786                  stanza_type=stanza_type,stanza_id=stanza_id,
    787                  error=error,error_cond=error_cond)
    788
    789 -    def copy(self):
    790          """ Return a copy of `self`.  """
    791          return MucIq(self)
    792
    793 -    def make_kick_request(self,nick,reason):
    794          """
    795          Make the iq stanza a MUC room participant kick request.
    796
    797          :Parameters:
    798              - `nick`: nickname of user to kick.
    799              - `reason`: reason of the kick.
    800          :Types:
    801              - `nick`: `unicode`
    802              - `reason`: `unicode`
    803
    804          :return: object describing the kick request details.
    805          :returntype: `MucItem`
    806          """
    807          self.clear_muc_child()
    808          self.muc_child=MucAdminQuery(parent=self.xmlnode)
    809          item=MucItem("none","none",nick=nick,reason=reason)
    810          self.muc_child.add_item(item)
    811          return self.muc_child
    812
    813 -    def free(self):
    814          """Free the data associated with this `MucIq` object."""
    815          self.muc_free()
    816          Iq.free(self)
    817
    818  # vi: sts=4 et sw=4
    819