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