1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18  """Presence XMPP stanza handling 
 19   
 20  Normative reference: 
 21    - `RFC 3920 <http://www.ietf.org/rfc/rfc3920.txt>`__ 
 22  """ 
 23   
 24  __revision__="$Id: presence.py 671 2007-05-09 12:05:52Z jajcus $" 
 25  __docformat__="restructuredtext en" 
 26   
 27  import libxml2 
 28   
 29  from pyxmpp.utils import to_utf8,from_utf8 
 30  from pyxmpp.stanza import Stanza 
 31  from pyxmpp.xmlextra import common_ns 
 32   
 33  presence_types=("available","unavailable","probe","subscribe","unsubscribe","subscribed", 
 34          "unsubscribed","invisible","error") 
 35   
 36  accept_responses={ 
 37          "subscribe": "subscribed", 
 38          "subscribed": "subscribe", 
 39          "unsubscribe": "unsubscribed", 
 40          "unsubscribed": "unsubscribe", 
 41          } 
 42   
 43  deny_responses={ 
 44          "subscribe": "unsubscribed", 
 45          "subscribed": "unsubscribe", 
 46          "unsubscribe": "subscribed", 
 47          "unsubscribed": "subscribe", 
 48          } 
 49   
 51      """Wraper object for <presence /> stanzas.""" 
 52      stanza_type="presence" 
 53 -    def __init__(self, xmlnode = None, from_jid = None, to_jid = None, stanza_type = None,  
 54              stanza_id = None, show = None, status = None, priority = 0, 
 55              error = None, error_cond = None, stream = None): 
  56          """Initialize a `Presence` object. 
 57   
 58          :Parameters: 
 59              - `xmlnode`: XML node to_jid be wrapped into the `Presence` object 
 60                or other Presence object to be copied. If not given then new 
 61                presence stanza is created using following parameters. 
 62              - `from_jid`: sender JID. 
 63              - `to_jid`: recipient JID. 
 64              - `stanza_type`: staza type: one of: None, "available", "unavailable", 
 65                "subscribe", "subscribed", "unsubscribe", "unsubscribed" or 
 66                "error". "available" is automaticaly changed to_jid None. 
 67              - `stanza_id`: stanza id -- value of stanza's "id" attribute 
 68              - `show`: "show" field of presence stanza. One of: None, "away", 
 69                "xa", "dnd", "chat". 
 70              - `status`: descriptive text for the presence stanza. 
 71              - `priority`: presence priority. 
 72              - `error_cond`: error condition name. Ignored if `stanza_type` is not "error" 
 73          :Types: 
 74              - `xmlnode`: `unicode` or `libxml2.xmlNode` or `Stanza` 
 75              - `from_jid`: `JID` 
 76              - `to_jid`: `JID` 
 77              - `stanza_type`: `unicode` 
 78              - `stanza_id`: `unicode` 
 79              - `show`: `unicode` 
 80              - `status`: `unicode` 
 81              - `priority`: `unicode` 
 82              - `error_cond`: `unicode`""" 
 83          self.xmlnode=None 
 84          if isinstance(xmlnode,Presence): 
 85              pass 
 86          elif isinstance(xmlnode,Stanza): 
 87              raise TypeError,"Couldn't make Presence from other Stanza" 
 88          elif isinstance(xmlnode,libxml2.xmlNode): 
 89              pass 
 90          elif xmlnode is not None: 
 91              raise TypeError,"Couldn't make Presence from %r" % (type(xmlnode),) 
 92   
 93          if stanza_type and stanza_type not in presence_types: 
 94              raise ValueError, "Invalid presence type: %r" % (type,) 
 95   
 96          if stanza_type=="available": 
 97              stanza_type=None 
 98   
 99          if xmlnode is None: 
100              xmlnode="presence" 
101   
102          Stanza.__init__(self, xmlnode, from_jid = from_jid, to_jid = to_jid, stanza_type = stanza_type, 
103                  stanza_id = stanza_id, error = error, error_cond = error_cond, stream = stream) 
104   
105          if show: 
106              self.xmlnode.newTextChild(common_ns,"show",to_utf8(show)) 
107          if status: 
108              self.xmlnode.newTextChild(common_ns,"status",to_utf8(status)) 
109          if priority and priority!=0: 
110              self.xmlnode.newTextChild(common_ns,"priority",to_utf8(unicode(priority))) 
 111   
113          """Create a deep copy of the presence stanza. 
114   
115          :returntype: `Presence`""" 
116          return Presence(self) 
 117   
119          """Change presence status description. 
120   
121          :Parameters: 
122              - `status`: descriptive text for the presence stanza. 
123          :Types: 
124              - `status`: `unicode`""" 
125          n=self.xpath_eval("ns:status") 
126          if not status: 
127              if n: 
128                  n[0].unlinkNode() 
129                  n[0].freeNode() 
130              else: 
131                  return 
132          if n: 
133              n[0].setContent(to_utf8(status)) 
134          else: 
135              self.xmlnode.newTextChild(common_ns,"status",to_utf8(status)) 
 136   
138          """Get presence status description. 
139   
140          :return: value of stanza's <status/> field. 
141          :returntype: `unicode`""" 
142          n=self.xpath_eval("ns:status") 
143          if n: 
144              return from_utf8(n[0].getContent()) 
145          else: 
146              return None 
 147   
149          """Get presence "show" field. 
150   
151          :return: value of stanza's <show/> field. 
152          :returntype: `unicode`""" 
153          n=self.xpath_eval("ns:show") 
154          if n: 
155              return from_utf8(n[0].getContent()) 
156          else: 
157              return None 
 158   
160          """Change presence "show" field. 
161   
162          :Parameters: 
163              - `show`: new value for the "show" field of presence stanza. One 
164                of: None, "away", "xa", "dnd", "chat". 
165          :Types: 
166              - `show`: `unicode`""" 
167          n=self.xpath_eval("ns:show") 
168          if not show: 
169              if n: 
170                  n[0].unlinkNode() 
171                  n[0].freeNode() 
172              else: 
173                  return 
174          if n: 
175              n[0].setContent(to_utf8(show)) 
176          else: 
177              self.xmlnode.newTextChild(common_ns,"show",to_utf8(show)) 
 178   
180          """Get presence priority. 
181   
182          :return: value of stanza's priority. 0 if the stanza doesn't contain 
183              <priority/> element. 
184          :returntype: `int`""" 
185          n=self.xpath_eval("ns:priority") 
186          if not n: 
187              return 0 
188          try: 
189              prio=int(n[0].getContent()) 
190          except ValueError: 
191              return 0 
192          return prio 
 193   
195          """Change presence priority. 
196   
197          :Parameters: 
198              - `priority`: new presence priority. 
199          :Types: 
200              - `priority`: `int`""" 
201          n=self.xpath_eval("ns:priority") 
202          if not priority: 
203              if n: 
204                  n[0].unlinkNode() 
205                  n[0].freeNode() 
206              else: 
207                  return 
208          priority=int(priority) 
209          if priority<-128 or priority>127: 
210              raise ValueError, "Bad priority value" 
211          priority=str(priority) 
212          if n: 
213              n[0].setContent(priority) 
214          else: 
215              self.xmlnode.newTextChild(common_ns,"priority",priority) 
 216   
218          """Create "accept" response for the "subscribe"/"subscribed"/"unsubscribe"/"unsubscribed" 
219          presence stanza. 
220   
221          :return: new stanza. 
222          :returntype: `Presence`""" 
223   
224          if self.get_type() not in ("subscribe","subscribed","unsubscribe","unsubscribed"): 
225              raise ValueError, ("Results may only be generated for 'subscribe'," 
226                  "'subscribed','unsubscribe' or 'unsubscribed' presence") 
227   
228          pr=Presence(stanza_type=accept_responses[self.get_type()], 
229              from_jid=self.get_to(),to_jid=self.get_from(),stanza_id=self.get_id()) 
230          return pr 
 231   
233          """Create "deny" response for the "subscribe"/"subscribed"/"unsubscribe"/"unsubscribed" 
234          presence stanza. 
235   
236          :return: new presence stanza. 
237          :returntype: `Presence`""" 
238          if self.get_type() not in ("subscribe","subscribed","unsubscribe","unsubscribed"): 
239              raise ValueError, ("Results may only be generated for 'subscribe'," 
240                  "'subscribed','unsubscribe' or 'unsubscribed' presence") 
241   
242          pr=Presence(stanza_type=deny_responses[self.get_type()], 
243              from_jid=self.get_to(),to_jid=self.get_from(),stanza_id=self.get_id()) 
244          return pr 
 245   
247          """Create error response for the any non-error presence stanza. 
248   
249          :Parameters: 
250              - `cond`: error condition name, as defined in XMPP specification. 
251          :Types: 
252              - `cond`: `unicode` 
253   
254          :return: new presence stanza. 
255          :returntype: `Presence`""" 
256   
257          if self.get_type() == "error": 
258              raise ValueError, "Errors may not be generated in response to errors" 
259   
260          p=Presence(stanza_type="error",from_jid=self.get_to(),to_jid=self.get_from(), 
261              stanza_id=self.get_id(),error_cond=cond) 
262   
263          if self.xmlnode.children: 
264              n=self.xmlnode.children 
265              while n: 
266                  p.xmlnode.children.addPrevSibling(n.copyNode(1)) 
267                  n=n.next 
268          return p 
  269   
270   
271