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