pyxmpp.jabberd.componentstream

Package pyxmpp :: Package jabberd :: Module componentstream |
[hide private]

# Source Code for Module pyxmpp.jabberd.componentstream

      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  # pylint: disable-msg=W0221, W0201
     18  """Component (jabber:component:accept) stream handling.
     19
     20  Normative reference:
     21    - `JEP 114 <http://www.jabber.org/jeps/jep-0114.html>`__
     22  """
     23
     24  __revision__="$Id: componentstream.py 703 2010-04-03 17:45:43Z jajcus $"
     25  __docformat__="restructuredtext en"
     26
     27  import hashlib
     28
     29  import logging
     30
     31  from pyxmpp.stream import Stream
     32  from pyxmpp.streambase import stanza_factory,HostMismatch
     33  from pyxmpp.xmlextra import common_doc,common_root
     34  from pyxmpp.utils import to_utf8
     35  from pyxmpp.exceptions import StreamError,FatalStreamError,ComponentStreamError,FatalComponentStreamError
     36
     37 -class ComponentStream(Stream):
     38      """Handles jabberd component (jabber:component:accept) connection stream.
     39
     40      :Ivariables:
     41          - `server`: server to use.
     42          - `port`: port number to use.
     43          - `secret`: authentication secret.
     44      :Types:
     45          - `server`: `unicode`
     46          - `port`: `int`
     47          - `secret`: `unicode`"""
     48
     49 -    def __init__(self, jid, secret, server, port, keepalive = 0, owner = None):
     50          """Initialize a `ComponentStream` object.
     51
     52          :Parameters:
     53              - `jid`: JID of the component.
     54              - `secret`: authentication secret.
     55              - `server`: server address.
     56              - `port`: TCP port number on the server.
     57              - `keepalive`: keepalive interval. 0 to disable.
     58              - `owner`: `Client`, `Component` or similar object "owning" this stream.
     59          """
     60          Stream.__init__(self, "jabber:component:accept",
     61                      sasl_mechanisms = [],
     62                      tls_settings = None,
     63                      keepalive = keepalive,
     64                      owner = owner)
     65          self.server=server
     66          self.port=port
     67          self.me=jid
     68          self.secret=secret
     69          self.process_all_stanzas=1
     70          self.__logger=logging.getLogger("pyxmpp.jabberd.ComponentStream")
     71
     72 -    def _reset(self):
     73          """Reset `ComponentStream` object state, making the object ready to
     74          handle new connections."""
     75          Stream._reset(self)
     76
     77 -    def connect(self,server=None,port=None):
     78          """Establish a client connection to a server.
     79
     80          [component only]
     81
     82          :Parameters:
     83              - `server`: name or address of the server to use.  If not given
     84                then use the one specified when creating the object.
     85              - `port`: port number of the server to use.  If not given then use
     86                the one specified when creating the object.
     87
     88          :Types:
     89              - `server`: `unicode`
     90              - `port`: `int`"""
     91          self.lock.acquire()
     92          try:
     93              self._connect(server,port)
     94          finally:
     95              self.lock.release()
     96
     97 -    def _connect(self,server=None,port=None):
     98          """Same as `ComponentStream.connect` but assume `self.lock` is acquired."""
     99          if self.me.node or self.me.resource:
    100              raise Value, "Component JID may have only domain defined"
    101          if not server:
    102              server=self.server
    103          if not port:
    104              port=self.port
    105          if not server or not port:
    106              raise ValueError, "Server or port not given"
    107          Stream._connect(self,server,port,None,self.me)
    108
    109 -    def accept(self,sock):
    110          """Accept an incoming component connection.
    111
    112          [server only]
    113
    114          :Parameters:
    115              - `sock`: a listening socket."""
    116          Stream.accept(self,sock,None)
    117
    118 -    def stream_start(self,doc):
    119          """Process <stream:stream> (stream start) tag received from peer.
    120
    121          Call `Stream.stream_start`, but ignore any `HostMismatch` error.
    122
    123          :Parameters:
    124              - `doc`: document created by the parser"""
    125          try:
    126              Stream.stream_start(self,doc)
    127          except HostMismatch:
    128              pass
    129
    130 -    def _post_connect(self):
    131          """Initialize authentication when the connection is established
    132          and we are the initiator."""
    133          if self.initiator:
    134              self._auth()
    135
    136 -    def _compute_handshake(self):
    137          """Compute the authentication handshake value.
    138
    139          :return: the computed hash value.
    140          :returntype: `str`"""
    141          return hashlib.sha1(to_utf8(self.stream_id)+to_utf8(self.secret)).hexdigest()
    142
    143 -    def _auth(self):
    144          """Authenticate on the server.
    145
    146          [component only]"""
    147          if self.authenticated:
    148              self.__logger.debug("_auth: already authenticated")
    149              return
    150          self.__logger.debug("doing handshake...")
    151          hash_value=self._compute_handshake()
    152          n=common_root.newTextChild(None,"handshake",hash_value)
    153          self._write_node(n)
    154          n.unlinkNode()
    155          n.freeNode()
    156          self.__logger.debug("handshake hash sent.")
    157
    158 -    def _process_node(self,node):
    159          """Process first level element of the stream.
    160
    161          Handle component handshake (authentication) element, and
    162          treat elements in "jabber:component:accept", "jabber:client"
    163          and "jabber:server" equally (pass to `self.process_stanza`).
    164          All other elements are passed to `Stream._process_node`.
    165
    166          :Parameters:
    167              - `node`: XML node describing the element
    168          """
    169          ns=node.ns()
    170          if ns:
    171              ns_uri=node.ns().getContent()
    172          if (not ns or ns_uri=="jabber:component:accept") and node.name=="handshake":
    173              if self.initiator and not self.authenticated:
    174                  self.authenticated=1
    175                  self.state_change("authenticated",self.me)
    176                  self._post_auth()
    177                  return
    178              elif not self.authenticated and node.getContent()==self._compute_handshake():
    179                  self.peer=self.me
    180                  n=common_doc.newChild(None,"handshake",None)
    181                  self._write_node(n)
    182                  n.unlinkNode()
    183                  n.freeNode()
    184                  self.peer_authenticated=1
    185                  self.state_change("authenticated",self.peer)
    186                  self._post_auth()
    187                  return
    188              else:
    189                  self._send_stream_error("not-authorized")
    190                  raise FatalComponentStreamError,"Hanshake error."
    191
    192          if ns_uri in ("jabber:component:accept","jabber:client","jabber:server"):
    193              stanza=stanza_factory(node)
    194              self.lock.release()
    195              try:
    196                  self.process_stanza(stanza)
    197              finally:
    198                  self.lock.acquire()
    199                  stanza.free()
    200              return
    201          return Stream._process_node(self,node)
    202
    203  # vi: sts=4 et sw=4
    204