1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 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 648 2006-08-26 20:09:37Z 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   
101          if self.xmlnode: 
102              self.free() 
 103   
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   
114          """ 
115          Detach the XML node borrowed by `self`. 
116          """ 
117          self.xmlnode=None 
 118   
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   
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): 
 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                   
203                  since = None 
204                  return HistoryParameters(maxchars, maxstanzas, maxseconds, since) 
 205   
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   
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   
268      """ 
269      Base class for <status/> and <item/> element wrappers. 
270      """ 
272          if self.__class__ is MucItemBase: 
273              raise RuntimeError,"Abstract class called" 
  274   
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   
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   
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          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   
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      """ 
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   
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   
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   
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   
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 
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               
508              n=n.next 
509          return ret 
 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 
 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   
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       
549      pass 
 550   
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   
563      """ 
564      Base class for MUC specific stanza extensions. Used together 
565      with one of stanza classes (Iq, Message or Presence). 
566      """ 
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   
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   
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   
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   
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   
656          """ 
657          Free MUC specific data. 
658          """ 
659          if self.muc_child: 
660              self.muc_child.free_borrowed() 
  661   
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   
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   
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   
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   
789          """ Return a copy of `self`.  """ 
790          return MucIq(self) 
 791   
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   
813          """Free the data associated with this `MucIq` object.""" 
814          self.muc_free() 
815          Iq.free(self) 
  816   
817   
818