Fix a bunch of issues in common.jingle*.

This commit is contained in:
Emmanuel Gil Peyrot 2017-02-08 02:49:33 +00:00
parent 57fb80f1fa
commit ad9370afa8
9 changed files with 527 additions and 440 deletions

View File

@ -142,6 +142,10 @@ SHOW_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd',
# zeroconf account name # zeroconf account name
ZEROCONF_ACC_NAME = 'Local' ZEROCONF_ACC_NAME = 'Local'
# These will be set in gajim.gui_interface.
idlequeue = None
socks5queue = None
HAVE_ZEROCONF = True HAVE_ZEROCONF = True
try: try:
__import__('avahi') __import__('avahi')

View File

@ -28,19 +28,21 @@ Handles the jingle signalling protocol
# * config: # * config:
# - codecs # - codecs
import logging
import nbxmpp import nbxmpp
from common import helpers from common import helpers
from common import gajim from common import gajim
from common.jingle_session import JingleSession, JingleStates from common.jingle_session import JingleSession, JingleStates
if gajim.HAVE_FARSTREAM:
from common.jingle_rtp import JingleAudio, JingleVideo
from common.jingle_ft import JingleFileTransfer from common.jingle_ft import JingleFileTransfer
from common.jingle_transport import JingleTransportSocks5, JingleTransportIBB from common.jingle_transport import JingleTransportSocks5, JingleTransportIBB
if gajim.HAVE_FARSTREAM:
from common.jingle_rtp import JingleAudio, JingleVideo
import logging
logger = logging.getLogger('gajim.c.jingle') logger = logging.getLogger('gajim.c.jingle')
class ConnectionJingle(object): class ConnectionJingle(object):
""" """
This object depends on that it is a part of Connection class. This object depends on that it is a part of Connection class.
@ -80,7 +82,7 @@ class ConnectionJingle(object):
try: try:
jid = helpers.get_full_jid_from_iq(stanza) jid = helpers.get_full_jid_from_iq(stanza)
except helpers.InvalidFormat: except helpers.InvalidFormat:
logger.warn('Invalid JID: %s, ignoring it' % stanza.getFrom()) logger.warning('Invalid JID: %s, ignoring it', stanza.getFrom())
return return
id_ = stanza.getID() id_ = stanza.getID()
if (jid, id_) in self.__iq_responses.keys(): if (jid, id_) in self.__iq_responses.keys():
@ -109,7 +111,7 @@ class ConnectionJingle(object):
self._sessions[sid].on_stanza(stanza) self._sessions[sid].on_stanza(stanza)
# Delete invalid/unneeded sessions # Delete invalid/unneeded sessions
if sid in self._sessions and \ if sid in self._sessions and \
self._sessions[sid].state == JingleStates.ended: self._sessions[sid].state == JingleStates.ENDED:
self.delete_jingle_session(sid) self.delete_jingle_session(sid)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
@ -142,7 +144,7 @@ class ConnectionJingle(object):
return jingle.sid return jingle.sid
def start_file_transfer(self, jid, file_props, request=False): def start_file_transfer(self, jid, file_props, request=False):
logger.info("start file transfer with file: %s" % file_props) logger.info("start file transfer with file: %s", file_props)
contact = gajim.contacts.get_contact_with_highest_priority(self.name, contact = gajim.contacts.get_contact_with_highest_priority(self.name,
gajim.get_jid_without_resource(jid)) gajim.get_jid_without_resource(jid))
if gajim.contacts.is_gc_contact(self.name, jid): if gajim.contacts.is_gc_contact(self.name, jid):
@ -162,7 +164,8 @@ class ConnectionJingle(object):
elif contact.supports(nbxmpp.NS_JINGLE_IBB): elif contact.supports(nbxmpp.NS_JINGLE_IBB):
transport = JingleTransportIBB() transport = JingleTransportIBB()
c = JingleFileTransfer(jingle, transport=transport, c = JingleFileTransfer(jingle, transport=transport,
file_props=file_props, use_security=use_security) file_props=file_props,
use_security=use_security)
file_props.algo = self.__hash_support(contact) file_props.algo = self.__hash_support(contact)
jingle.add_content('file' + helpers.get_random_string_16(), c) jingle.add_content('file' + helpers.get_random_string_16(), c)
jingle.start_session() jingle.start_session()

View File

@ -37,7 +37,7 @@ class JingleContentSetupException(Exception):
""" """
class JingleContent(object): class JingleContent:
""" """
An abstraction of content in Jingle sessions An abstraction of content in Jingle sessions
""" """
@ -48,8 +48,8 @@ class JingleContent(object):
# will be filled by JingleSession.add_content() # will be filled by JingleSession.add_content()
# don't uncomment these lines, we will catch more buggy code then # don't uncomment these lines, we will catch more buggy code then
# (a JingleContent not added to session shouldn't send anything) # (a JingleContent not added to session shouldn't send anything)
#self.creator = None self.creator = None
#self.name = None self.name = None
self.accepted = False self.accepted = False
self.sent = False self.sent = False
self.negotiated = False self.negotiated = False
@ -59,6 +59,10 @@ class JingleContent(object):
self.senders = 'both' #FIXME self.senders = 'both' #FIXME
self.allow_sending = True # Used for stream direction, attribute 'senders' self.allow_sending = True # Used for stream direction, attribute 'senders'
# These were found by the Politie
self.file_props = None
self.use_security = None
self.callbacks = { self.callbacks = {
# these are called when *we* get stanzas # these are called when *we* get stanzas
'content-accept': [self.__on_transport_info, 'content-accept': [self.__on_transport_info,
@ -191,17 +195,16 @@ class JingleContent(object):
file_tag.addChild(node=node) file_tag.addChild(node=node)
if self.file_props.type_ == 'r': if self.file_props.type_ == 'r':
if self.file_props.hash_: if self.file_props.hash_:
h = file_tag.addChild('hash', attrs={ file_tag.addChild('hash', attrs={'algo': self.file_props.algo},
'algo': self.file_props.algo}, namespace=nbxmpp.NS_HASHES, namespace=nbxmpp.NS_HASHES,
payload=self.file_props.hash_) payload=self.file_props.hash_)
else: else:
# if the file is less than 10 mb, then it is small # if the file is less than 10 mb, then it is small
# lets calculate it right away # lets calculate it right away
if self.file_props.size < 10000000 and not \ if self.file_props.size < 10000000 and not self.file_props.hash_:
self.file_props.hash_: hash_data = content._compute_hash()
h = self._calcHash() if hash_data:
if h: file_tag.addChild(node=hash_data)
file_tag.addChild(node=h)
pjid = gajim.get_jid_without_resource(self.session.peerjid) pjid = gajim.get_jid_without_resource(self.session.peerjid)
file_info = {'name' : self.file_props.name, file_info = {'name' : self.file_props.name,
'file-name' : self.file_props.file_name, 'file-name' : self.file_props.file_name,
@ -223,9 +226,9 @@ class JingleContent(object):
cert = load_cert_file(certpath) cert = load_cert_file(certpath)
if cert: if cert:
try: try:
digest_algo = cert.get_signature_algorithm().decode('utf-8' digest_algo = (cert.get_signature_algorithm()
).split('With')[0] .decode('utf-8').split('With')[0])
except AttributeError as e: except AttributeError:
# Old py-OpenSSL is missing get_signature_algorithm # Old py-OpenSSL is missing get_signature_algorithm
digest_algo = "sha256" digest_algo = "sha256"
security.addChild('fingerprint').addData(cert.digest( security.addChild('fingerprint').addData(cert.digest(
@ -240,5 +243,3 @@ class JingleContent(object):
def destroy(self): def destroy(self):
self.callbacks = None self.callbacks = None
del self.session.contents[(self.creator, self.name)] del self.session.contents[(self.creator, self.name)]

View File

@ -19,33 +19,39 @@
Handles Jingle File Transfer (XEP 0234) Handles Jingle File Transfer (XEP 0234)
""" """
import os
import hashlib import hashlib
from common import gajim import logging
import os
import threading
from enum import IntEnum
import nbxmpp import nbxmpp
from common import gajim
from common import configpaths from common import configpaths
from . import jingle_xtls from common import jingle_xtls
from common.jingle_content import contents, JingleContent from common.jingle_content import contents, JingleContent
from common.jingle_transport import * from common.jingle_transport import JingleTransportSocks5, TransportType
from common import helpers from common import helpers
from common.connection_handlers_events import FileRequestReceivedEvent from common.connection_handlers_events import FileRequestReceivedEvent
import threading from common.jingle_ftstates import (
import logging StateInitialized, StateCandSent, StateCandReceived, StateTransfering,
from common.jingle_ftstates import * StateCandSentAndRecv, StateTransportReplace)
log = logging.getLogger('gajim.c.jingle_ft') log = logging.getLogger('gajim.c.jingle_ft')
STATE_NOT_STARTED = 0
STATE_INITIALIZED = 1 class State(IntEnum):
NOT_STARTED = 0
INITIALIZED = 1
# We send the candidates and we are waiting for a reply # We send the candidates and we are waiting for a reply
STATE_CAND_SENT = 2 CAND_SENT = 2
# We received the candidates and we are waiting to reply # We received the candidates and we are waiting to reply
STATE_CAND_RECEIVED = 3 CAND_RECEIVED = 3
# We have sent and received the candidates # We have sent and received the candidates
# This also includes any candidate-error received or sent # This also includes any candidate-error received or sent
STATE_CAND_SENT_AND_RECEIVED = 4 CAND_SENT_AND_RECEIVED = 4
STATE_TRANSPORT_REPLACE = 5 TRANSPORT_REPLACE = 5
# We are transfering the file # We are transfering the file
STATE_TRANSFERING = 6 TRANSFERING = 6
class JingleFileTransfer(JingleContent): class JingleFileTransfer(JingleContent):
@ -53,7 +59,7 @@ class JingleFileTransfer(JingleContent):
def __init__(self, session, transport=None, file_props=None, def __init__(self, session, transport=None, file_props=None,
use_security=False): use_security=False):
JingleContent.__init__(self, session, transport) JingleContent.__init__(self, session, transport)
log.info("transport value: %s" % transport) log.info("transport value: %s", transport)
# events we might be interested in # events we might be interested in
self.callbacks['session-initiate'] += [self.__on_session_initiate] self.callbacks['session-initiate'] += [self.__on_session_initiate]
self.callbacks['session-initiate-sent'] += [ self.callbacks['session-initiate-sent'] += [
@ -85,13 +91,13 @@ class JingleFileTransfer(JingleContent):
self.file_props.sid = session.sid self.file_props.sid = session.sid
self.file_props.transfered_size = [] self.file_props.transfered_size = []
self.file_props.transport_sid = self.transport.sid self.file_props.transport_sid = self.transport.sid
log.info("FT request: %s" % file_props) log.info("FT request: %s", file_props)
if transport is None: if transport is None:
self.transport = JingleTransportSocks5() self.transport = JingleTransportSocks5()
self.transport.set_connection(session.connection) self.transport.set_connection(session.connection)
self.transport.set_file_props(self.file_props) self.transport.set_file_props(self.file_props)
self.transport.set_our_jid(session.ourjid) self.transport.set_our_jid(session.ourjid)
log.info('ourjid: %s' % session.ourjid) log.info('ourjid: %s', session.ourjid)
self.session = session self.session = session
self.media = 'file' self.media = 'file'
self.nominated_cand = {} self.nominated_cand = {}
@ -99,15 +105,17 @@ class JingleFileTransfer(JingleContent):
session.peerjid): session.peerjid):
roomjid = session.peerjid.split('/')[0] roomjid = session.peerjid.split('/')[0]
dstaddr = hashlib.sha1(('%s%s%s' % (self.file_props.sid, dstaddr = hashlib.sha1(('%s%s%s' % (self.file_props.sid,
session.ourjid, roomjid)).encode('utf-8')).hexdigest() session.ourjid, roomjid))
.encode('utf-8')).hexdigest()
self.file_props.dstaddr = dstaddr self.file_props.dstaddr = dstaddr
self.state = STATE_NOT_STARTED self.state = State.NOT_STARTED
self.states = {STATE_INITIALIZED : StateInitialized(self), self.states = {
STATE_CAND_SENT : StateCandSent(self), State.INITIALIZED : StateInitialized(self),
STATE_CAND_RECEIVED : StateCandReceived(self), State.CAND_SENT : StateCandSent(self),
STATE_TRANSFERING : StateTransfering(self), State.CAND_RECEIVED : StateCandReceived(self),
STATE_TRANSPORT_REPLACE : StateTransportReplace(self), State.TRANSFERING : StateTransfering(self),
STATE_CAND_SENT_AND_RECEIVED : StateCandSentAndRecv(self) State.TRANSPORT_REPLACE : StateTransportReplace(self),
State.CAND_SENT_AND_RECEIVED : StateCandSentAndRecv(self)
} }
if jingle_xtls.PYOPENSSL_PRESENT: if jingle_xtls.PYOPENSSL_PRESENT:
@ -129,7 +137,9 @@ class JingleFileTransfer(JingleContent):
def __on_session_initiate(self, stanza, content, error, action): def __on_session_initiate(self, stanza, content, error, action):
log.debug("Jingle FT request received") log.debug("Jingle FT request received")
gajim.nec.push_incoming_event(FileRequestReceivedEvent(None, gajim.nec.push_incoming_event(FileRequestReceivedEvent(None,
conn=self.session.connection, stanza=stanza, jingle_content=content, conn=self.session.connection,
stanza=stanza,
jingle_content=content,
FT_content=self)) FT_content=self))
if self.session.request: if self.session.request:
# accept the request # accept the request
@ -141,8 +151,9 @@ class JingleFileTransfer(JingleContent):
def __send_hash(self): def __send_hash(self):
# Send hash in a session info # Send hash in a session info
checksum = nbxmpp.Node(tag='checksum', payload=[nbxmpp.Node(tag='file', checksum = nbxmpp.Node(tag='checksum',
payload=[self._calcHash()])]) payload=[nbxmpp.Node(tag='file',
payload=[self._compute_hash()])])
checksum.setNamespace(nbxmpp.NS_JINGLE_FILE_TRANSFER) checksum.setNamespace(nbxmpp.NS_JINGLE_FILE_TRANSFER)
self.session.__session_info(checksum) self.session.__session_info(checksum)
pjid = gajim.get_jid_without_resource(self.session.peerjid) pjid = gajim.get_jid_without_resource(self.session.peerjid)
@ -155,13 +166,13 @@ class JingleFileTransfer(JingleContent):
} }
self.session.connection.set_file_info(file_info) self.session.connection.set_file_info(file_info)
def _calcHash(self): def _compute_hash(self):
# Caculates the hash and returns a xep-300 hash stanza # Caculates the hash and returns a xep-300 hash stanza
if self.file_props.algo == None: if self.file_props.algo is None:
return return
try: try:
file_ = open(self.file_props.file_name, 'rb') file_ = open(self.file_props.file_name, 'rb')
except: except IOError:
# can't open file # can't open file
return return
h = nbxmpp.Hashes() h = nbxmpp.Hashes()
@ -196,26 +207,27 @@ class JingleFileTransfer(JingleContent):
id_ = jingle_xtls.send_cert_request(con, id_ = jingle_xtls.send_cert_request(con,
self.session.responder) self.session.responder)
jingle_xtls.key_exchange_pend(id_, jingle_xtls.key_exchange_pend(id_,
self.continue_session_accept, [stanza]) self.continue_session_accept,
[stanza])
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
self.continue_session_accept(stanza) self.continue_session_accept(stanza)
def continue_session_accept(self, stanza): def continue_session_accept(self, stanza):
con = self.session.connection con = self.session.connection
if self.state == STATE_TRANSPORT_REPLACE: if self.state == State.TRANSPORT_REPLACE:
# If we are requesting we don't have the file # If we are requesting we don't have the file
if self.session.werequest: if self.session.werequest:
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
# We send the file # We send the file
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
self.file_props.streamhosts = self.transport.remote_candidates self.file_props.streamhosts = self.transport.remote_candidates
# Calculate file hash in a new thread # Calculate file hash in a new thread
# if we haven't sent the hash already. # if we haven't sent the hash already.
if self.file_props.hash_ is None and self.file_props.algo and \ if self.file_props.hash_ is None and self.file_props.algo and \
not self.werequest: not self.werequest:
self.hashThread = threading.Thread(target=self.__send_hash) self.hash_thread = threading.Thread(target=self.__send_hash)
self.hashThread.start() self.hash_thread.start()
for host in self.file_props.streamhosts: for host in self.file_props.streamhosts:
host['initiator'] = self.session.initiator host['initiator'] = self.session.initiator
host['target'] = self.session.responder host['target'] = self.session.responder
@ -225,11 +237,13 @@ class JingleFileTransfer(JingleContent):
fingerprint = 'client' fingerprint = 'client'
if self.transport.type_ == TransportType.SOCKS5: if self.transport.type_ == TransportType.SOCKS5:
gajim.socks5queue.connect_to_hosts(self.session.connection.name, gajim.socks5queue.connect_to_hosts(self.session.connection.name,
self.file_props.sid, self.on_connect, self.file_props.sid,
self._on_connect_error, fingerprint=fingerprint, self.on_connect,
self._on_connect_error,
fingerprint=fingerprint,
receiving=False) receiving=False)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
def __on_session_terminate(self, stanza, content, error, action): def __on_session_terminate(self, stanza, content, error, action):
@ -249,78 +263,78 @@ class JingleFileTransfer(JingleContent):
def __on_transport_info(self, stanza, content, error, action): def __on_transport_info(self, stanza, content, error, action):
log.info("__on_transport_info") log.info("__on_transport_info")
candError = content.getTag('transport').getTag('candidate-error') cand_error = content.getTag('transport').getTag('candidate-error')
candUsed = content.getTag('transport').getTag('candidate-used') cand_used = content.getTag('transport').getTag('candidate-used')
if (candError or candUsed) and \ if (cand_error or cand_used) and \
self.state >= STATE_CAND_SENT_AND_RECEIVED: self.state >= State.CAND_SENT_AND_RECEIVED:
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
if candError: if cand_error:
if not gajim.socks5queue.listener.connections: if not gajim.socks5queue.listener.connections:
gajim.socks5queue.listener.disconnect() gajim.socks5queue.listener.disconnect()
self.nominated_cand['peer-cand'] = False self.nominated_cand['peer-cand'] = False
if self.state == STATE_CAND_SENT: if self.state == State.CAND_SENT:
if not self.nominated_cand['our-cand'] and \ if not self.nominated_cand['our-cand'] and \
not self.nominated_cand['peer-cand']: not self.nominated_cand['peer-cand']:
if not self.weinitiate: if not self.weinitiate:
return return
self.__state_changed(STATE_TRANSPORT_REPLACE) self.__state_changed(State.TRANSPORT_REPLACE)
else: else:
response = stanza.buildReply('result') response = stanza.buildReply('result')
response.delChild(response.getQuery()) response.delChild(response.getQuery())
self.session.connection.connection.send(response) self.session.connection.connection.send(response)
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
else: else:
args = {'candError' : True} args = {'cand_error' : True}
self.__state_changed(STATE_CAND_RECEIVED, args) self.__state_changed(State.CAND_RECEIVED, args)
return return
if candUsed: if cand_used:
streamhost_cid = candUsed.getAttr('cid') streamhost_cid = cand_used.getAttr('cid')
streamhost_used = None streamhost_used = None
for cand in self.transport.candidates: for cand in self.transport.candidates:
if cand['candidate_id'] == streamhost_cid: if cand['candidate_id'] == streamhost_cid:
streamhost_used = cand streamhost_used = cand
break break
if streamhost_used == None or streamhost_used['type'] == 'proxy': if streamhost_used is None or streamhost_used['type'] == 'proxy':
if gajim.socks5queue.listener and \ if gajim.socks5queue.listener and \
not gajim.socks5queue.listener.connections: not gajim.socks5queue.listener.connections:
gajim.socks5queue.listener.disconnect() gajim.socks5queue.listener.disconnect()
if content.getTag('transport').getTag('activated'): if content.getTag('transport').getTag('activated'):
self.state = STATE_TRANSFERING self.state = State.TRANSFERING
jid = gajim.get_jid_without_resource(self.session.ourjid) jid = gajim.get_jid_without_resource(self.session.ourjid)
gajim.socks5queue.send_file(self.file_props, gajim.socks5queue.send_file(self.file_props,
self.session.connection.name, 'client') self.session.connection.name, 'client')
return return
args = {'content': content, args = {'content': content,
'sendCand': False} 'sendCand': False}
if self.state == STATE_CAND_SENT: if self.state == State.CAND_SENT:
self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args) self.__state_changed(State.CAND_SENT_AND_RECEIVED, args)
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
else: else:
self.__state_changed(STATE_CAND_RECEIVED, args) self.__state_changed(State.CAND_RECEIVED, args)
def __on_iq_result(self, stanza, content, error, action): def __on_iq_result(self, stanza, content, error, action):
log.info("__on_iq_result") log.info("__on_iq_result")
if self.state == STATE_NOT_STARTED: if self.state == State.NOT_STARTED:
self.__state_changed(STATE_INITIALIZED) self.__state_changed(State.INITIALIZED)
elif self.state == STATE_CAND_SENT_AND_RECEIVED: elif self.state == State.CAND_SENT_AND_RECEIVED:
if not self.nominated_cand['our-cand'] and \ if not self.nominated_cand['our-cand'] and \
not self.nominated_cand['peer-cand']: not self.nominated_cand['peer-cand']:
if not self.weinitiate: if not self.weinitiate:
return return
self.__state_changed(STATE_TRANSPORT_REPLACE) self.__state_changed(State.TRANSPORT_REPLACE)
return return
# initiate transfer # initiate transfer
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
def __transport_setup(self, stanza=None, content=None, error=None, def __transport_setup(self, stanza=None, content=None, error=None,
action=None): action=None):
# Sets up a few transport specific things for the file transfer # Sets up a few transport specific things for the file transfer
if self.transport.type_ == TransportType.IBB: if self.transport.type_ == TransportType.IBB:
# No action required, just set the state to transfering # No action required, just set the state to transfering
self.state = STATE_TRANSFERING self.state = State.TRANSFERING
else: else:
self._listen_host() self._listen_host()
@ -334,19 +348,19 @@ class JingleFileTransfer(JingleContent):
args = {'streamhost' : streamhost, args = {'streamhost' : streamhost,
'sendCand' : True} 'sendCand' : True}
self.nominated_cand['our-cand'] = streamhost self.nominated_cand['our-cand'] = streamhost
self.__sendCand(args) self.__send_candidate(args)
def _on_connect_error(self, sid): def _on_connect_error(self, sid):
log.info('connect error, sid=' + sid) log.info('connect error, sid=' + sid)
args = {'candError' : True, args = {'candError' : True,
'sendCand' : True} 'sendCand' : True}
self.__sendCand(args) self.__send_candidate(args)
def __sendCand(self, args): def __send_candidate(self, args):
if self.state == STATE_CAND_RECEIVED: if self.state == State.CAND_RECEIVED:
self.__state_changed(STATE_CAND_SENT_AND_RECEIVED, args) self.__state_changed(State.CAND_SENT_AND_RECEIVED, args)
else: else:
self.__state_changed(STATE_CAND_SENT, args) self.__state_changed(State.CAND_SENT, args)
def _store_socks5_sid(self, sid, hash_id): def _store_socks5_sid(self, sid, hash_id):
# callback from socsk5queue.start_listener # callback from socsk5queue.start_listener
@ -362,38 +376,34 @@ class JingleFileTransfer(JingleContent):
fingerprint = None fingerprint = None
if self.use_security: if self.use_security:
fingerprint = 'server' fingerprint = 'server'
if self.weinitiate:
listener = gajim.socks5queue.start_listener(port, sha_str, listener = gajim.socks5queue.start_listener(port, sha_str,
self._store_socks5_sid, self.file_props, self._store_socks5_sid,
fingerprint=fingerprint, typ='sender') self.file_props,
else: fingerprint=fingerprint,
listener = gajim.socks5queue.start_listener(port, sha_str, typ='sender' if self.weinitiate else 'receiver')
self._store_socks5_sid, self.file_props,
fingerprint=fingerprint, typ='receiver')
if not listener: if not listener:
# send error message, notify the user # send error message, notify the user
return return
def isOurCandUsed(self): def is_our_candidate_used(self):
''' '''
If this method returns true then the candidate we nominated will be If this method returns true then the candidate we nominated will be
used, if false, the candidate nominated by peer will be used used, if false, the candidate nominated by peer will be used
''' '''
if self.nominated_cand['peer-cand'] == False: if not self.nominated_cand['peer-cand']:
return True return True
if self.nominated_cand['our-cand'] == False: if not self.nominated_cand['our-cand']:
return False return False
peer_pr = int(self.nominated_cand['peer-cand']['priority']) peer_pr = int(self.nominated_cand['peer-cand']['priority'])
our_pr = int(self.nominated_cand['our-cand']['priority']) our_pr = int(self.nominated_cand['our-cand']['priority'])
if peer_pr != our_pr: if peer_pr != our_pr:
return our_pr > peer_pr return our_pr > peer_pr
else:
return self.weinitiate return self.weinitiate
def start_IBB_transfer(self): def start_ibb_transfer(self):
if self.file_props.type_ == 's': if self.file_props.type_ == 's':
self.__state_changed(STATE_TRANSFERING) self.__state_changed(State.TRANSFERING)
def get_content(desc): def get_content(desc):

View File

@ -13,11 +13,14 @@
## You should have received a copy of the GNU General Public License ## You should have received a copy of the GNU General Public License
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
from common import gajim
import nbxmpp import nbxmpp
from common.jingle_transport import * from common import gajim
from common.jingle_transport import TransportType
from common.socks5 import Socks5ReceiverClient, Socks5SenderClient from common.socks5 import Socks5ReceiverClient, Socks5SenderClient
import logging
log = logging.getLogger('gajim.c.jingle_ftstates')
class JingleFileTransferStates: class JingleFileTransferStates:
''' '''
@ -31,7 +34,7 @@ class JingleFileTransferStates:
''' '''
This method MUST be overriden by a subclass This method MUST be overriden by a subclass
''' '''
raise Exception('This is an abstract method!!') raise NotImplementedError('This is an abstract method!')
class StateInitialized(JingleFileTransferStates): class StateInitialized(JingleFileTransferStates):
@ -50,8 +53,10 @@ class StateInitialized(JingleFileTransferStates):
fingerprint = 'client' fingerprint = 'client'
# Connect to the candidate host, on success call on_connect method # Connect to the candidate host, on success call on_connect method
gajim.socks5queue.connect_to_hosts(self.jft.session.connection.name, gajim.socks5queue.connect_to_hosts(self.jft.session.connection.name,
self.jft.file_props.sid, self.jft.on_connect, self.jft.file_props.sid,
self.jft._on_connect_error, fingerprint=fingerprint) self.jft.on_connect,
self.jft._on_connect_error,
fingerprint=fingerprint)
class StateCandSent(JingleFileTransferStates): class StateCandSent(JingleFileTransferStates):
@ -59,7 +64,7 @@ class StateCandSent(JingleFileTransferStates):
This state sends our nominated candidate This state sends our nominated candidate
''' '''
def _sendCand(self, args): def _send_candidate(self, args):
if 'candError' in args: if 'candError' in args:
self.jft.nominated_cand['our-cand'] = False self.jft.nominated_cand['our-cand'] = False
self.jft.send_error_candidate() self.jft.send_error_candidate()
@ -80,7 +85,7 @@ class StateCandSent(JingleFileTransferStates):
self.jft.session.send_transport_info(content) self.jft.session.send_transport_info(content)
def action(self, args=None): def action(self, args=None):
self._sendCand(args) self._send_candidate(args)
class StateCandReceived(JingleFileTransferStates): class StateCandReceived(JingleFileTransferStates):
@ -89,7 +94,7 @@ class StateCandReceived(JingleFileTransferStates):
It takes the arguments: canError if we receive a candidate-error It takes the arguments: canError if we receive a candidate-error
''' '''
def _recvCand(self, args): def _recv_candidate(self, args):
if 'candError' in args: if 'candError' in args:
return return
content = args['content'] content = args['content']
@ -100,14 +105,14 @@ class StateCandReceived(JingleFileTransferStates):
if cand['candidate_id'] == streamhost_cid: if cand['candidate_id'] == streamhost_cid:
streamhost_used = cand streamhost_used = cand
break break
if streamhost_used == None: if streamhost_used is None:
log.info("unknow streamhost") log.info("unknow streamhost")
return return
# We save the candidate nominated by peer # We save the candidate nominated by peer
self.jft.nominated_cand['peer-cand'] = streamhost_used self.jft.nominated_cand['peer-cand'] = streamhost_used
def action(self, args=None): def action(self, args=None):
self._recvCand(args) self._recv_candidate(args)
class StateCandSentAndRecv(StateCandSent, StateCandReceived): class StateCandSentAndRecv(StateCandSent, StateCandReceived):
@ -119,9 +124,9 @@ class StateCandSentAndRecv( StateCandSent, StateCandReceived):
def action(self, args=None): def action(self, args=None):
if args['sendCand']: if args['sendCand']:
self._sendCand(args) self._send_candidate(args)
else: else:
self._recvCand(args) self._recv_candidate(args)
class StateTransportReplace(JingleFileTransferStates): class StateTransportReplace(JingleFileTransferStates):
@ -139,16 +144,16 @@ class StateTransfering(JingleFileTransferStates):
we have. we have.
''' '''
def __start_IBB_transfer(self, con): def _start_ibb_transfer(self, con):
self.jft.file_props.transport_sid = self.jft.transport.sid self.jft.file_props.transport_sid = self.jft.transport.sid
fp = open(self.jft.file_props.file_name, 'r') fp = open(self.jft.file_props.file_name, 'r')
con.OpenStream(self.jft.file_props.sid, self.jft.session.peerjid, fp, con.OpenStream(self.jft.file_props.sid, self.jft.session.peerjid, fp,
blocksize=4096) blocksize=4096)
def __start_SOCK5_transfer(self): def _start_sock5_transfer(self):
# It tells wether we start the transfer as client or server # It tells wether we start the transfer as client or server
mode = None mode = None
if self.jft.isOurCandUsed(): if self.jft.is_our_candidate_used():
mode = 'client' mode = 'client'
streamhost_used = self.jft.nominated_cand['our-cand'] streamhost_used = self.jft.nominated_cand['our-cand']
gajim.socks5queue.remove_server(self.jft.file_props.sid) gajim.socks5queue.remove_server(self.jft.file_props.sid)
@ -193,12 +198,14 @@ class StateTransfering(JingleFileTransferStates):
sockobj = Socks5SenderClient(gajim.idlequeue, idx, sockobj = Socks5SenderClient(gajim.idlequeue, idx,
gajim.socks5queue, _sock=None, gajim.socks5queue, _sock=None,
host=str(streamhost_used['host']), host=str(streamhost_used['host']),
port=int(streamhost_used['port']), fingerprint=None, port=int(streamhost_used['port']),
connected=False, file_props=self.jft.file_props) fingerprint=None, connected=False,
file_props=self.jft.file_props)
else: else:
sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used, sockobj = Socks5ReceiverClient(gajim.idlequeue, streamhost_used,
sid=self.jft.file_props.sid, sid=self.jft.file_props.sid,
file_props=self.jft.file_props, fingerprint=None) file_props=self.jft.file_props,
fingerprint=None)
sockobj.proxy = True sockobj.proxy = True
sockobj.streamhost = streamhost_used sockobj.streamhost = streamhost_used
gajim.socks5queue.add_sockobj(self.jft.session.connection.name, gajim.socks5queue.add_sockobj(self.jft.session.connection.name,
@ -206,7 +213,7 @@ class StateTransfering(JingleFileTransferStates):
streamhost_used['idx'] = sockobj.queue_idx streamhost_used['idx'] = sockobj.queue_idx
# If we offered the nominated candidate used, we activate # If we offered the nominated candidate used, we activate
# the proxy # the proxy
if not self.jft.isOurCandUsed(): if not self.jft.is_our_candidate_used():
gajim.socks5queue.on_success[self.jft.file_props.sid] = \ gajim.socks5queue.on_success[self.jft.file_props.sid] = \
self.jft.transport._on_proxy_auth_ok self.jft.transport._on_proxy_auth_ok
# TODO: add on failure # TODO: add on failure
@ -217,8 +224,6 @@ class StateTransfering(JingleFileTransferStates):
def action(self, args=None): def action(self, args=None):
if self.jft.transport.type_ == TransportType.IBB: if self.jft.transport.type_ == TransportType.IBB:
self.__start_IBB_transfer(self.jft.session.connection) self._start_ibb_transfer(self.jft.session.connection)
elif self.jft.transport.type_ == TransportType.SOCKS5: elif self.jft.transport.type_ == TransportType.SOCKS5:
self.__start_SOCK5_transfer() self._start_sock5_transfer()

View File

@ -17,6 +17,7 @@
Handles Jingle RTP sessions (XEP 0167) Handles Jingle RTP sessions (XEP 0167)
""" """
import logging
import socket import socket
import nbxmpp import nbxmpp
import gi import gi
@ -35,7 +36,6 @@ from common.jingle_session import FailedApplication
from collections import deque from collections import deque
import logging
log = logging.getLogger('gajim.c.jingle_rtp') log = logging.getLogger('gajim.c.jingle_rtp')
@ -46,7 +46,8 @@ class JingleRTPContent(JingleContent):
JingleContent.__init__(self, session, transport) JingleContent.__init__(self, session, transport)
self.media = media self.media = media
self._dtmf_running = False self._dtmf_running = False
self.farstream_media = {'audio': Farstream.MediaType.AUDIO, self.farstream_media = {
'audio': Farstream.MediaType.AUDIO,
'video': Farstream.MediaType.VIDEO}[media] 'video': Farstream.MediaType.VIDEO}[media]
self.pipeline = None self.pipeline = None
@ -55,6 +56,12 @@ class JingleRTPContent(JingleContent):
self.candidates_ready = False # True when local candidates are prepared self.candidates_ready = False # True when local candidates are prepared
# TODO
self.conference = None
self.funnel = None
self.p2psession = None
self.p2pstream = None
self.callbacks['session-initiate'] += [self.__on_remote_codecs] self.callbacks['session-initiate'] += [self.__on_remote_codecs]
self.callbacks['content-add'] += [self.__on_remote_codecs] self.callbacks['content-add'] += [self.__on_remote_codecs]
self.callbacks['description-info'] += [self.__on_remote_codecs] self.callbacks['description-info'] += [self.__on_remote_codecs]
@ -92,7 +99,7 @@ class JingleRTPContent(JingleContent):
ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC, ip = socket.getaddrinfo(stun_server, 0, socket.AF_UNSPEC,
socket.SOCK_STREAM)[0][4][0] socket.SOCK_STREAM)[0][4][0]
except socket.gaierror as e: except socket.gaierror as e:
log.warning('Lookup of stun ip failed: %s' % str(e)) log.warning('Lookup of stun ip failed: %s', str(e))
else: else:
params['stun-ip'] = ip params['stun-ip'] = ip
@ -102,20 +109,20 @@ class JingleRTPContent(JingleContent):
self.p2pstream.set_transmitter_ht('nice', params) self.p2pstream.set_transmitter_ht('nice', params)
def is_ready(self): def is_ready(self):
return (JingleContent.is_ready(self) and self.candidates_ready) return JingleContent.is_ready(self) and self.candidates_ready
def make_bin_from_config(self, config_key, pipeline, text): def make_bin_from_config(self, config_key, pipeline, text):
pipeline = pipeline % gajim.config.get(config_key) pipeline = pipeline % gajim.config.get(config_key)
try: try:
bin = Gst.parse_bin_from_description(pipeline, True) gst_bin = Gst.parse_bin_from_description(pipeline, True)
return bin return gst_bin
except GLib.GError as e: except GLib.GError as e:
gajim.nec.push_incoming_event(InformationEvent(None, gajim.nec.push_incoming_event(
conn=self.session.connection, level='error', InformationEvent(
None, conn=self.session.connection, level='error',
pri_txt=_('%s configuration error') % text.capitalize(), pri_txt=_('%s configuration error') % text.capitalize(),
sec_txt=_("Couldn't setup %s. Check your configuration.\n\n" sec_txt=_('Couldnt setup %s. Check your configuration.\n\n'
"Pipeline was:\n%s\n\nError was:\n%s") % (text, pipeline, 'Pipeline was:\n%s\n\nError was:\n%s') % (text, pipeline, str(e))))
str(e))))
raise JingleContentSetupException raise JingleContentSetupException
def add_remote_candidates(self, candidates): def add_remote_candidates(self, candidates):
@ -157,7 +164,8 @@ class JingleRTPContent(JingleContent):
def _fill_content(self, content): def _fill_content(self, content):
content.addChild(nbxmpp.NS_JINGLE_RTP + ' description', content.addChild(nbxmpp.NS_JINGLE_RTP + ' description',
attrs={'media': self.media}, payload=list(self.iter_codecs())) attrs={'media': self.media},
payload=list(self.iter_codecs()))
def _setup_funnel(self): def _setup_funnel(self):
self.funnel = Gst.ElementFactory.make('funnel', None) self.funnel = Gst.ElementFactory.make('funnel', None)
@ -174,7 +182,7 @@ class JingleRTPContent(JingleContent):
def _on_gst_message(self, bus, message): def _on_gst_message(self, bus, message):
if message.type == Gst.MessageType.ELEMENT: if message.type == Gst.MessageType.ELEMENT:
name = message.get_structure().get_name() name = message.get_structure().get_name()
log.debug('gst element message: %s: %s' % (name, message)) log.debug('gst element message: %s: %s', name, message)
if name == 'farstream-new-active-candidate-pair': if name == 'farstream-new-active-candidate-pair':
pass pass
elif name == 'farstream-recv-codecs-changed': elif name == 'farstream-recv-codecs-changed':
@ -208,18 +216,20 @@ class JingleRTPContent(JingleContent):
reason.setTag('failed-transport') reason.setTag('failed-transport')
self.session.remove_content(self.creator, self.name, reason) self.session.remove_content(self.creator, self.name, reason)
elif name == 'farstream-error': elif name == 'farstream-error':
log.error('Farstream error #%d!\nMessage: %s' % ( log.error('Farstream error #%d!\nMessage: %s',
message.get_structure().get_value('error-no'), message.get_structure().get_value('error-no'),
message.get_structure().get_value('error-msg'))) message.get_structure().get_value('error-msg'))
elif message.type == Gst.MessageType.ERROR: elif message.type == Gst.MessageType.ERROR:
# TODO: Fix it to fallback to videotestsrc anytime an error occur, # TODO: Fix it to fallback to videotestsrc anytime an error occur,
# or raise an error, Jingle way # or raise an error, Jingle way
# or maybe one-sided stream? # or maybe one-sided stream?
if not self.stream_failed_once: if not self.stream_failed_once:
gajim.nec.push_incoming_event(InformationEvent(None, gajim.nec.push_incoming_event(
conn=self.session.connection, level='error', InformationEvent(
pri_txt=_('GStreamer error'), sec_txt=_('Error: %s\nDebug: ' None, conn=self.session.connection, level='error',
'%s' % (message.get_structure().get_value('gerror'), pri_txt=_('GStreamer error'),
sec_txt=_('Error: %s\nDebug: %s' %
(message.get_structure().get_value('gerror'),
message.get_structure().get_value('debug'))))) message.get_structure().get_value('debug')))))
sink_pad = self.p2psession.get_property('sink-pad') sink_pad = self.p2psession.get_property('sink-pad')
@ -243,7 +253,8 @@ class JingleRTPContent(JingleContent):
# Start playing again # Start playing again
self.pipeline.set_state(Gst.State.PLAYING) self.pipeline.set_state(Gst.State.PLAYING)
def get_fallback_src(self): @staticmethod
def get_fallback_src():
return Gst.ElementFactory.make('fakesrc', None) return Gst.ElementFactory.make('fakesrc', None)
def on_negotiated(self): def on_negotiated(self):
@ -288,14 +299,17 @@ class JingleRTPContent(JingleContent):
def iter_codecs(self): def iter_codecs(self):
codecs = self.p2psession.props.codecs_without_config codecs = self.p2psession.props.codecs_without_config
for codec in codecs: for codec in codecs:
attrs = {'name': codec.encoding_name, attrs = {
'name': codec.encoding_name,
'id': codec.id, 'id': codec.id,
'channels': codec.channels} 'channels': codec.channels
}
if codec.clock_rate: if codec.clock_rate:
attrs['clockrate'] = codec.clock_rate attrs['clockrate'] = codec.clock_rate
if codec.optional_params: if codec.optional_params:
payload = list(nbxmpp.Node('parameter', {'name': p.name, payload = [nbxmpp.Node('parameter',
'value': p.value}) for p in codec.optional_params) {'name': p.name, 'value': p.value})
for p in codec.optional_params]
else: else:
payload = [] payload = []
yield nbxmpp.Node('payload-type', attrs, payload) yield nbxmpp.Node('payload-type', attrs, payload)
@ -343,7 +357,8 @@ class JingleAudio(JingleRTPContent):
# place 16kHz before 8kHz, as buggy psi versions will take in # place 16kHz before 8kHz, as buggy psi versions will take in
# account only the first codec # account only the first codec
codecs = [Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX', codecs = [
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
Farstream.MediaType.AUDIO, 16000), Farstream.MediaType.AUDIO, 16000),
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX', Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
Farstream.MediaType.AUDIO, 8000)] Farstream.MediaType.AUDIO, 8000)]
@ -352,10 +367,12 @@ class JingleAudio(JingleRTPContent):
# the local parts # the local parts
# TODO: Add queues? # TODO: Add queues?
self.src_bin = self.make_bin_from_config('audio_input_device', self.src_bin = self.make_bin_from_config('audio_input_device',
'%s ! audioconvert', _("audio input")) '%s ! audioconvert',
_("audio input"))
self.sink = self.make_bin_from_config('audio_output_device', self.sink = self.make_bin_from_config('audio_output_device',
'audioconvert ! volume name=gajim_out_vol ! %s', _("audio output")) 'audioconvert ! volume name=gajim_out_vol ! %s',
_("audio output"))
self.mic_volume = self.src_bin.get_by_name('gajim_vol') self.mic_volume = self.src_bin.get_by_name('gajim_vol')
self.out_volume = self.sink.get_by_name('gajim_out_vol') self.out_volume = self.sink.get_by_name('gajim_out_vol')
@ -411,8 +428,9 @@ class JingleVideo(JingleRTPContent):
tee = '' tee = ''
self.src_bin = self.make_bin_from_config('video_input_device', self.src_bin = self.make_bin_from_config('video_input_device',
'%%s %s! %svideoscale ! %svideoconvert' % (tee, framerate, '%%s %s! %svideoscale ! %svideoconvert' %
video_size), _("video input")) (tee, framerate, video_size),
_("video input"))
self.pipeline.add(self.src_bin) self.pipeline.add(self.src_bin)
self.pipeline.set_state(Gst.State.PLAYING) self.pipeline.set_state(Gst.State.PLAYING)

View File

@ -28,24 +28,28 @@ Handles Jingle sessions (XEP 0166)
# - Tie-breaking # - Tie-breaking
# * timeout # * timeout
from common import gajim
import nbxmpp
from common.jingle_transport import get_jingle_transport, JingleTransportIBB
from common.jingle_content import get_jingle_content, JingleContentSetupException
from common.jingle_content import JingleContent
from common.jingle_ft import STATE_TRANSPORT_REPLACE
from common.connection_handlers_events import *
import logging import logging
from enum import Enum
import nbxmpp
from common import gajim
from common.jingle_transport import get_jingle_transport, JingleTransportIBB
from common.jingle_content import get_jingle_content, JingleContentSetupException, JingleContent
from common.jingle_ft import State
from common.connection_handlers_events import (
FilesProp, JingleRequestReceivedEvent, JingleDisconnectedReceivedEvent,
JingleTransferCancelledEvent, JingleConnectedReceivedEvent,
JingleErrorReceivedEvent)
log = logging.getLogger("gajim.c.jingle_session") log = logging.getLogger("gajim.c.jingle_session")
# FIXME: Move it to JingleSession.States? # FIXME: Move it to JingleSession.States?
class JingleStates(object): class JingleStates(Enum):
""" """
States in which jingle session may exist States in which jingle session may exist
""" """
ended = 0 ENDED = 0
pending = 1 PENDING = 1
active = 2 ACTIVE = 2
class OutOfOrder(Exception): class OutOfOrder(Exception):
""" """
@ -61,10 +65,11 @@ class TieBreak(Exception):
class FailedApplication(Exception): class FailedApplication(Exception):
""" """
Exception that should be raised in case responder supports none of the payload-types offered by the initiator Exception that should be raised in case responder supports none of the
payload-types offered by the initiator
""" """
class JingleSession(object): class JingleSession:
""" """
This represents one jingle session, that is, one or more content types This represents one jingle session, that is, one or more content types
negotiated between an initiator and a responder. negotiated between an initiator and a responder.
@ -85,16 +90,16 @@ class JingleSession(object):
self.ourjid = self.ourjid + '/' + con.server_resource self.ourjid = self.ourjid + '/' + con.server_resource
self.peerjid = jid # jid we connect to self.peerjid = jid # jid we connect to
# jid we use as the initiator # jid we use as the initiator
self.initiator = weinitiate and self.ourjid or self.peerjid self.initiator = self.ourjid if weinitiate else self.peerjid
# jid we use as the responder # jid we use as the responder
self.responder = weinitiate and self.peerjid or self.ourjid self.responder = self.peerjid if weinitiate else self.ourjid
# are we an initiator? # are we an initiator?
self.weinitiate = weinitiate self.weinitiate = weinitiate
# Are we requesting or offering a file? # Are we requesting or offering a file?
self.werequest = werequest self.werequest = werequest
self.request = False self.request = False
# what state is session in? (one from JingleStates) # what state is session in? (one from JingleStates)
self.state = JingleStates.ended self.state = JingleStates.ENDED
if not sid: if not sid:
sid = con.connection.getAnID() sid = con.connection.getAnID()
self.sid = sid # sessionid self.sid = sid # sessionid
@ -106,7 +111,7 @@ class JingleSession(object):
self.iq_ids = [] self.iq_ids = []
self.accepted = True # is this session accepted by user self.accepted = True # is this session accepted by user
# Tells whether this session is a file transfer or not # Tells whether this session is a file transfer or not
self.session_type_FT = False self.session_type_ft = False
# callbacks to call on proper contents # callbacks to call on proper contents
# use .prepend() to add new callbacks, especially when you're going # use .prepend() to add new callbacks, especially when you're going
# to send error instead of ack # to send error instead of ack
@ -174,7 +179,7 @@ class JingleSession(object):
def reject_content(self, media, name=None): def reject_content(self, media, name=None):
content = self.get_content(media, name) content = self.get_content(media, name)
if content: if content:
if self.state == JingleStates.active: if self.state == JingleStates.ACTIVE:
self.__content_reject(content) self.__content_reject(content)
content.destroy() content.destroy()
self.on_session_state_changed() self.on_session_state_changed()
@ -184,7 +189,7 @@ class JingleSession(object):
Called when user stops or cancel session in UI Called when user stops or cancel session in UI
""" """
reason = nbxmpp.Node('reason') reason = nbxmpp.Node('reason')
if self.state == JingleStates.active: if self.state == JingleStates.ACTIVE:
reason.addChild('success') reason.addChild('success')
else: else:
reason.addChild('cancel') reason.addChild('cancel')
@ -245,11 +250,11 @@ class JingleSession(object):
content.accepted = True content.accepted = True
def on_session_state_changed(self, content=None): def on_session_state_changed(self, content=None):
if self.state == JingleStates.ended: if self.state == JingleStates.ENDED:
# Session not yet started, only one action possible: session-initiate # Session not yet started, only one action possible: session-initiate
if self.is_ready() and self.weinitiate: if self.is_ready() and self.weinitiate:
self.__session_initiate() self.__session_initiate()
elif self.state == JingleStates.pending: elif self.state == JingleStates.PENDING:
# We can either send a session-accept or a content-add # We can either send a session-accept or a content-add
if self.is_ready() and not self.weinitiate: if self.is_ready() and not self.weinitiate:
self.__session_accept() self.__session_accept()
@ -257,7 +262,7 @@ class JingleSession(object):
self.__content_add(content) self.__content_add(content)
elif content and self.weinitiate: elif content and self.weinitiate:
self.__content_accept(content) self.__content_accept(content)
elif self.state == JingleStates.active: elif self.state == JingleStates.ACTIVE:
# We can either send a content-add or a content-accept. However, if # We can either send a content-add or a content-accept. However, if
# we are sending a file we can only use session_initiate. # we are sending a file we can only use session_initiate.
if not content: if not content:
@ -298,20 +303,20 @@ class JingleSession(object):
pass pass
def send_content_accept(self, content): def send_content_accept(self, content):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-accept') stanza, jingle = self.__make_jingle('content-accept')
jingle.addChild(node=content) jingle.addChild(node=content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
def send_transport_info(self, content): def send_transport_info(self, content):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('transport-info') stanza, jingle = self.__make_jingle('transport-info')
jingle.addChild(node=content) jingle.addChild(node=content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID()) self.collect_iq_id(stanza.getID())
def send_description_info(self, content): def send_description_info(self, content):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('description-info') stanza, jingle = self.__make_jingle('description-info')
jingle.addChild(node=content) jingle.addChild(node=content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
@ -335,7 +340,7 @@ class JingleSession(object):
return return
# FIXME: If we aren't initiated and it's not a session-initiate... # FIXME: If we aren't initiated and it's not a session-initiate...
if action not in ['session-initiate', 'session-terminate'] \ if action not in ['session-initiate', 'session-terminate'] \
and self.state == JingleStates.ended: and self.state == JingleStates.ENDED:
self.__send_error(stanza, 'item-not-found', 'unknown-session') self.__send_error(stanza, 'item-not-found', 'unknown-session')
return return
else: else:
@ -381,7 +386,7 @@ class JingleSession(object):
transport = JingleTransportIBB() transport = JingleTransportIBB()
# For debug only, delete this and replace for a function # For debug only, delete this and replace for a function
# that will identify contents by its sid # that will identify contents by its sid
for creator, name in self.contents.keys(): for creator, name in self.contents:
self.modify_content(creator, name, transport) self.modify_content(creator, name, transport)
cont = self.contents[(creator, name)] cont = self.contents[(creator, name)]
cont.transport = transport cont.transport = transport
@ -389,7 +394,7 @@ class JingleSession(object):
self.__append_contents(jingle) self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'transport-replace') self.__broadcast(stanza, jingle, None, 'transport-replace')
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
self.state = JingleStates.pending self.state = JingleStates.PENDING
def __on_transport_replace(self, stanza, jingle, error, action): def __on_transport_replace(self, stanza, jingle, error, action):
for content in jingle.iterTags('content'): for content in jingle.iterTags('content'):
@ -405,8 +410,8 @@ class JingleSession(object):
elif transport_ns == nbxmpp.NS_JINGLE_IBB: elif transport_ns == nbxmpp.NS_JINGLE_IBB:
transport = JingleTransportIBB() transport = JingleTransportIBB()
self.modify_content(creator, name, transport) self.modify_content(creator, name, transport)
self.state = JingleStates.pending self.state = JingleStates.PENDING
self.contents[(creator,name)].state = STATE_TRANSPORT_REPLACE self.contents[(creator, name)].state = State.TRANSPORT_REPLACE
self.__ack(stanza, jingle, error, action) self.__ack(stanza, jingle, error, action)
self.__session_accept() self.__session_accept()
self.contents[(creator, name)].start_IBB_transfer() self.contents[(creator, name)].start_IBB_transfer()
@ -421,9 +426,9 @@ class JingleSession(object):
# FIXME: This ressource is unknown to us, what should we do? # FIXME: This ressource is unknown to us, what should we do?
# For now, reject the transport # For now, reject the transport
stanza, jingle = self.__make_jingle('transport-reject') stanza, jingle = self.__make_jingle('transport-reject')
c = jingle.setTag('content', attrs={'creator': creator, content = jingle.setTag('content', attrs={'creator': creator,
'name': name}) 'name': name})
c.setTag('transport', namespace=transport_ns) content.setTag('transport', namespace=transport_ns)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
@ -433,11 +438,11 @@ class JingleSession(object):
if payload[0].getName() == 'ringing': if payload[0].getName() == 'ringing':
# ignore ringing # ignore ringing
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
if self.state != JingleStates.active: if self.state != JingleStates.ACTIVE:
raise OutOfOrder raise OutOfOrder
for p in payload: for child in payload:
if p.getName() == 'checksum': if child.getName() == 'checksum':
hash_ = p.getTag('file').getTag(name='hash', hash_ = child.getTag('file').getTag(name='hash',
namespace=nbxmpp.NS_HASHES) namespace=nbxmpp.NS_HASHES)
algo = hash_.getAttr('algo') algo = hash_.getAttr('algo')
if algo in nbxmpp.Hashes.supported: if algo in nbxmpp.Hashes.supported:
@ -468,11 +473,12 @@ class JingleSession(object):
def __on_session_accept(self, stanza, jingle, error, action): def __on_session_accept(self, stanza, jingle, error, action):
# FIXME # FIXME
if self.state != JingleStates.pending: if self.state != JingleStates.PENDING:
raise OutOfOrder raise OutOfOrder
self.state = JingleStates.active self.state = JingleStates.ACTIVE
def __on_content_accept(self, stanza, jingle, error, action): @staticmethod
def __on_content_accept(stanza, jingle, error, action):
""" """
Called when we get content-accept stanza or equivalent one (like Called when we get content-accept stanza or equivalent one (like
session-accept) session-accept)
@ -484,7 +490,7 @@ class JingleSession(object):
name = content['name'] name = content['name']
def __on_content_add(self, stanza, jingle, error, action): def __on_content_add(self, stanza, jingle, error, action):
if self.state == JingleStates.ended: if self.state == JingleStates.ENDED:
raise OutOfOrder raise OutOfOrder
parse_result = self.__parse_contents(jingle) parse_result = self.__parse_contents(jingle)
contents = parse_result[0] contents = parse_result[0]
@ -496,14 +502,16 @@ class JingleSession(object):
self.__content_reject(content) self.__content_reject(content)
self.contents[(content.creator, content.name)].destroy() self.contents[(content.creator, content.name)].destroy()
gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None, gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None,
conn=self.connection, jingle_session=self, contents=contents)) conn=self.connection,
jingle_session=self,
contents=contents))
def __on_session_initiate(self, stanza, jingle, error, action): def __on_session_initiate(self, stanza, jingle, error, action):
""" """
We got a jingle session request from other entity, therefore we are the We got a jingle session request from other entity, therefore we are the
receiver... Unpack the data, inform the user receiver... Unpack the data, inform the user
""" """
if self.state != JingleStates.ended: if self.state != JingleStates.ENDED:
raise OutOfOrder raise OutOfOrder
self.initiator = jingle['initiator'] self.initiator = jingle['initiator']
self.responder = self.ourjid self.responder = self.ourjid
@ -533,18 +541,17 @@ class JingleSession(object):
jingle.getTag('content').getTag('description').getTag('request') jingle.getTag('content').getTag('description').getTag('request')
if request: if request:
self.request = True self.request = True
h = request.getTag('file').getTag('hash') hash_tag = request.getTag('file').getTag('hash')
h = h.getData() if h else None hash_data = hash_tag.getData() if hash_tag else None
n = request.getTag('file').getTag('name') n = request.getTag('file').getTag('name')
n = n.getData() if n else None n = n.getData() if n else None
pjid = gajim.get_jid_without_resource(self.peerjid) pjid = gajim.get_jid_without_resource(self.peerjid)
file_info = self.connection.get_file_info(pjid, h, n, file_info = self.connection.get_file_info(pjid, hash_data, n,
self.connection.name) self.connection.name)
if not file_info: if not file_info:
log.warning('The peer ' + pjid + \ log.warning('The peer %s is requesting a ' \
' is requesting a ' + \ 'file that we dont have or ' \
'file that we dont have or ' + \ 'it is not allowed to request', pjid)
'it is not allowed to request')
self.decline_session() self.decline_session()
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
# If there's no content we understand... # If there's no content we understand...
@ -555,10 +562,12 @@ class JingleSession(object):
self.__ack(stanza, jingle, error, action) self.__ack(stanza, jingle, error, action)
self._session_terminate(reason) self._session_terminate(reason)
raise nbxmpp.NodeProcessed raise nbxmpp.NodeProcessed
self.state = JingleStates.pending self.state = JingleStates.PENDING
# Send event about starting a session # Send event about starting a session
gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None, gajim.nec.push_incoming_event(JingleRequestReceivedEvent(None,
conn=self.connection, jingle_session=self, contents=contents)) conn=self.connection,
jingle_session=self,
contents=contents))
def __broadcast(self, stanza, jingle, error, action): def __broadcast(self, stanza, jingle, error, action):
""" """
@ -594,9 +603,11 @@ class JingleSession(object):
else: else:
# TODO # TODO
text = reason text = reason
if reason == 'cancel' and self.session_type_FT: if reason == 'cancel' and self.session_type_ft:
gajim.nec.push_incoming_event(JingleTransferCancelledEvent(None, gajim.nec.push_incoming_event(JingleTransferCancelledEvent(None,
conn=self.connection, jingle_session=self, media=None, conn=self.connection,
jingle_session=self,
media=None,
reason=text)) reason=text))
def __broadcast_all(self, stanza, jingle, error, action): def __broadcast_all(self, stanza, jingle, error, action):
@ -645,17 +656,21 @@ class JingleSession(object):
text = '%s (%s)' % (error, text) text = '%s (%s)' % (error, text)
if type_ != 'modify': if type_ != 'modify':
gajim.nec.push_incoming_event(JingleErrorReceivedEvent(None, gajim.nec.push_incoming_event(JingleErrorReceivedEvent(None,
conn=self.connection, jingle_session=self, conn=self.connection,
jingle_session=self,
reason=text or error)) reason=text or error))
def __reason_from_stanza(self, stanza): @staticmethod
def __reason_from_stanza(stanza):
# TODO: Move to GUI? # TODO: Move to GUI?
reason = 'success' reason = 'success'
reasons = ['success', 'busy', 'cancel', 'connectivity-error', reasons = [
'decline', 'expired', 'failed-application', 'failed-transport', 'success', 'busy', 'cancel', 'connectivity-error', 'decline',
'expired', 'failed-application', 'failed-transport',
'general-error', 'gone', 'incompatible-parameters', 'media-error', 'general-error', 'gone', 'incompatible-parameters', 'media-error',
'security-error', 'timeout', 'unsupported-applications', 'security-error', 'timeout', 'unsupported-applications',
'unsupported-transports'] 'unsupported-transports'
]
tag = stanza.getTag('reason') tag = stanza.getTag('reason')
text = '' text = ''
if tag: if tag:
@ -669,9 +684,11 @@ class JingleSession(object):
def __make_jingle(self, action, reason=None): def __make_jingle(self, action, reason=None):
stanza = nbxmpp.Iq(typ='set', to=nbxmpp.JID(self.peerjid), stanza = nbxmpp.Iq(typ='set', to=nbxmpp.JID(self.peerjid),
frm=self.ourjid) frm=self.ourjid)
attrs = {'action': action, attrs = {
'action': action,
'sid': self.sid, 'sid': self.sid,
'initiator' : self.initiator} 'initiator' : self.initiator
}
jingle = stanza.addChild('jingle', attrs=attrs, jingle = stanza.addChild('jingle', attrs=attrs,
namespace=nbxmpp.NS_JINGLE) namespace=nbxmpp.NS_JINGLE)
if reason is not None: if reason is not None:
@ -690,13 +707,15 @@ class JingleSession(object):
self.connection.connection.send(err_stanza) self.connection.connection.send(err_stanza)
self.__dispatch_error(jingle_error or error, text, type_) self.__dispatch_error(jingle_error or error, text, type_)
def __append_content(self, jingle, content): @staticmethod
def __append_content(jingle, content):
""" """
Append <content/> element to <jingle/> element, with (full=True) or Append <content/> element to <jingle/> element, with (full=True) or
without (full=False) <content/> children without (full=False) <content/> children
""" """
jingle.addChild('content', jingle.addChild('content',
attrs={'name': content.name, 'creator': content.creator}) attrs={'name': content.name,
'creator': content.creator})
def __append_contents(self, jingle): def __append_contents(self, jingle):
""" """
@ -709,36 +728,36 @@ class JingleSession(object):
self.__append_content(jingle, content) self.__append_content(jingle, content)
def __session_initiate(self): def __session_initiate(self):
assert self.state == JingleStates.ended assert self.state == JingleStates.ENDED
stanza, jingle = self.__make_jingle('session-initiate') stanza, jingle = self.__make_jingle('session-initiate')
self.__append_contents(jingle) self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'session-initiate-sent') self.__broadcast(stanza, jingle, None, 'session-initiate-sent')
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID()) self.collect_iq_id(stanza.getID())
self.state = JingleStates.pending self.state = JingleStates.PENDING
def __session_accept(self): def __session_accept(self):
assert self.state == JingleStates.pending assert self.state == JingleStates.PENDING
stanza, jingle = self.__make_jingle('session-accept') stanza, jingle = self.__make_jingle('session-accept')
self.__append_contents(jingle) self.__append_contents(jingle)
self.__broadcast(stanza, jingle, None, 'session-accept-sent') self.__broadcast(stanza, jingle, None, 'session-accept-sent')
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
self.collect_iq_id(stanza.getID()) self.collect_iq_id(stanza.getID())
self.state = JingleStates.active self.state = JingleStates.ACTIVE
def __session_info(self, payload=None): def __session_info(self, payload=None):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('session-info') stanza, jingle = self.__make_jingle('session-info')
if payload: if payload:
jingle.addChild(node=payload) jingle.addChild(node=payload)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
def _JingleFileTransfer__session_info(self, p): def _JingleFileTransfer__session_info(self, payload):
# For some strange reason when I call # For some strange reason when I call
# self.session.__session_info(h) from the jingleFileTransfer object # self.session.__session_info(payload) from the jingleFileTransfer object
# within a thread, this method gets called instead. Even though, it # within a thread, this method gets called instead. Even though, it
# isn't being called explicitly. # isn't being called explicitly.
self.__session_info(p) self.__session_info(payload)
def _session_terminate(self, reason=None): def _session_terminate(self, reason=None):
stanza, jingle = self.__make_jingle('session-terminate', reason=reason) stanza, jingle = self.__make_jingle('session-terminate', reason=reason)
@ -755,12 +774,14 @@ class JingleSession(object):
text = reason text = reason
self.connection.delete_jingle_session(self.sid) self.connection.delete_jingle_session(self.sid)
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None, gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=None, conn=self.connection,
jingle_session=self,
media=None,
reason=text)) reason=text))
def __content_add(self, content): def __content_add(self, content):
# TODO: test # TODO: test
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-add') stanza, jingle = self.__make_jingle('content-add')
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.__broadcast(stanza, jingle, None, 'content-add-sent') self.__broadcast(stanza, jingle, None, 'content-add-sent')
@ -769,7 +790,7 @@ class JingleSession(object):
def __content_accept(self, content): def __content_accept(self, content):
# TODO: test # TODO: test
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-accept') stanza, jingle = self.__make_jingle('content-accept')
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.__broadcast(stanza, jingle, None, 'content-accept-sent') self.__broadcast(stanza, jingle, None, 'content-accept-sent')
@ -777,31 +798,35 @@ class JingleSession(object):
self.collect_iq_id(id_) self.collect_iq_id(id_)
def __content_reject(self, content): def __content_reject(self, content):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
stanza, jingle = self.__make_jingle('content-reject') stanza, jingle = self.__make_jingle('content-reject')
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
# TODO: this will fail if content is not an RTP content # TODO: this will fail if content is not an RTP content
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None, gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=content.media, conn=self.connection,
jingle_session=self,
media=content.media,
reason='rejected')) reason='rejected'))
def __content_modify(self): def __content_modify(self):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
def __content_remove(self, content, reason=None): def __content_remove(self, content, reason=None):
assert self.state != JingleStates.ended assert self.state != JingleStates.ENDED
if self.connection.connection and self.connection.connected > 1: if self.connection.connection and self.connection.connected > 1:
stanza, jingle = self.__make_jingle('content-remove', reason=reason) stanza, jingle = self.__make_jingle('content-remove', reason=reason)
self.__append_content(jingle, content) self.__append_content(jingle, content)
self.connection.connection.send(stanza) self.connection.connection.send(stanza)
# TODO: this will fail if content is not an RTP content # TODO: this will fail if content is not an RTP content
gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None, gajim.nec.push_incoming_event(JingleDisconnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=content.media, conn=self.connection,
jingle_session=self,
media=content.media,
reason='removed')) reason='removed'))
def content_negotiated(self, media): def content_negotiated(self, media):
gajim.nec.push_incoming_event(JingleConnectedReceivedEvent(None, gajim.nec.push_incoming_event(JingleConnectedReceivedEvent(None,
conn=self.connection, jingle_session=self, media=media)) conn=self.connection,
jingle_session=self,
media=media))

View File

@ -17,11 +17,11 @@
Handles Jingle Transports (currently only ICE-UDP) Handles Jingle Transports (currently only ICE-UDP)
""" """
import nbxmpp
import socket
from common import gajim
import logging import logging
import socket
from enum import IntEnum from enum import IntEnum
import nbxmpp
from common import gajim
log = logging.getLogger('gajim.c.jingle_transport') log = logging.getLogger('gajim.c.jingle_transport')
@ -43,16 +43,24 @@ class TransportType(IntEnum):
IBB = 3 IBB = 3
class JingleTransport(object): class JingleTransport:
""" """
An abstraction of a transport in Jingle sessions An abstraction of a transport in Jingle sessions
""" """
__slots__ = ['type_', 'candidates', 'remote_candidates', 'connection',
'file_props', 'ourjid', 'sid']
def __init__(self, type_): def __init__(self, type_):
self.type_ = type_ self.type_ = type_
self.candidates = [] self.candidates = []
self.remote_candidates = [] self.remote_candidates = []
self.connection = None
self.file_props = None
self.ourjid = None
self.sid = None
def _iter_candidates(self): def _iter_candidates(self):
for candidate in self.candidates: for candidate in self.candidates:
yield self.make_candidate(candidate) yield self.make_candidate(candidate)
@ -110,9 +118,7 @@ class JingleTransportSocks5(JingleTransport):
def make_candidate(self, candidate): def make_candidate(self, candidate):
import logging log.info('candidate dict, %s', candidate)
log = logging.getLogger()
log.info('candidate dict, %s' % candidate)
attrs = { attrs = {
'cid': candidate['candidate_id'], 'cid': candidate['candidate_id'],
'host': candidate['host'], 'host': candidate['host'],
@ -186,30 +192,34 @@ class JingleTransportSocks5(JingleTransport):
hosts = set() hosts = set()
local_ip_cand = [] local_ip_cand = []
c = {'host': self.connection.peerhost[0], candidate = {
'host': self.connection.peerhost[0],
'candidate_id': self.connection.connection.getAnID(), 'candidate_id': self.connection.connection.getAnID(),
'port': port, 'port': port,
'type': 'direct', 'type': 'direct',
'jid': self.ourjid, 'jid': self.ourjid,
'priority': priority} 'priority': priority
}
hosts.add(self.connection.peerhost[0]) hosts.add(self.connection.peerhost[0])
local_ip_cand.append(c) local_ip_cand.append(candidate)
try: try:
for addrinfo in socket.getaddrinfo(socket.gethostname(), None): for addrinfo in socket.getaddrinfo(socket.gethostname(), None):
addr = addrinfo[4][0] addr = addrinfo[4][0]
if not addr in hosts and not addr.startswith('127.') and \ if not addr in hosts and not addr.startswith('127.') and \
addr != '::1': addr != '::1':
c = {'host': addr, candidate = {
'host': addr,
'candidate_id': self.connection.connection.getAnID(), 'candidate_id': self.connection.connection.getAnID(),
'port': port, 'port': port,
'type': 'direct', 'type': 'direct',
'jid': self.ourjid, 'jid': self.ourjid,
'priority': priority, 'priority': priority,
'initiator': self.file_props.sender, 'initiator': self.file_props.sender,
'target': self.file_props.receiver} 'target': self.file_props.receiver
}
hosts.add(addr) hosts.add(addr)
local_ip_cand.append(c) local_ip_cand.append(candidate)
except socket.gaierror: except socket.gaierror:
pass # ignore address-related errors for getaddrinfo pass # ignore address-related errors for getaddrinfo
@ -226,16 +236,18 @@ class JingleTransportSocks5(JingleTransport):
if ft_add_hosts: if ft_add_hosts:
hosts = [e.strip() for e in ft_add_hosts.split(',')] hosts = [e.strip() for e in ft_add_hosts.split(',')]
for h in hosts: for host in hosts:
c = {'host': h, candidate = {
'host': host,
'candidate_id': self.connection.connection.getAnID(), 'candidate_id': self.connection.connection.getAnID(),
'port': port, 'port': port,
'type': 'direct', 'type': 'direct',
'jid': self.ourjid, 'jid': self.ourjid,
'priority': priority, 'priority': priority,
'initiator': self.file_props.sender, 'initiator': self.file_props.sender,
'target': self.file_props.receiver} 'target': self.file_props.receiver
additional_ip_cand.append(c) }
additional_ip_cand.append(candidate)
self._add_candidates(additional_ip_cand) self._add_candidates(additional_ip_cand)
@ -252,15 +264,17 @@ class JingleTransportSocks5(JingleTransport):
self.file_props.proxyhosts = proxyhosts self.file_props.proxyhosts = proxyhosts
for proxyhost in proxyhosts: for proxyhost in proxyhosts:
c = {'host': proxyhost['host'], candidate = {
'host': proxyhost['host'],
'candidate_id': self.connection.connection.getAnID(), 'candidate_id': self.connection.connection.getAnID(),
'port': int(proxyhost['port']), 'port': int(proxyhost['port']),
'type': 'proxy', 'type': 'proxy',
'jid': proxyhost['jid'], 'jid': proxyhost['jid'],
'priority': priority, 'priority': priority,
'initiator': self.file_props.sender, 'initiator': self.file_props.sender,
'target': self.file_props.receiver} 'target': self.file_props.receiver
proxy_cand.append(c) }
proxy_cand.append(candidate)
self._add_candidates(proxy_cand) self._add_candidates(proxy_cand)
@ -294,8 +308,8 @@ class JingleTransportSocks5(JingleTransport):
content = nbxmpp.Node('content') content = nbxmpp.Node('content')
content.setAttr('creator', 'initiator') content.setAttr('creator', 'initiator')
c = self.get_content() content_object = self.get_content()
content.setAttr('name', c.name) content.setAttr('name', content_object.name)
transport = nbxmpp.Node('transport') transport = nbxmpp.Node('transport')
transport.setNamespace(nbxmpp.NS_JINGLE_BYTESTREAM) transport.setNamespace(nbxmpp.NS_JINGLE_BYTESTREAM)
transport.setAttr('sid', proxy['sid']) transport.setAttr('sid', proxy['sid'])
@ -345,7 +359,7 @@ class JingleTransportIBB(JingleTransport):
try: try:
from gi.repository import Farstream from gi.repository import Farstream
except Exception: except ImportError:
pass pass
class JingleTransportICEUDP(JingleTransport): class JingleTransportICEUDP(JingleTransport):
@ -353,11 +367,13 @@ class JingleTransportICEUDP(JingleTransport):
JingleTransport.__init__(self, TransportType.ICEUDP) JingleTransport.__init__(self, TransportType.ICEUDP)
def make_candidate(self, candidate): def make_candidate(self, candidate):
types = {Farstream.CandidateType.HOST: 'host', types = {
Farstream.CandidateType.HOST: 'host',
Farstream.CandidateType.SRFLX: 'srflx', Farstream.CandidateType.SRFLX: 'srflx',
Farstream.CandidateType.PRFLX: 'prflx', Farstream.CandidateType.PRFLX: 'prflx',
Farstream.CandidateType.RELAY: 'relay', Farstream.CandidateType.RELAY: 'relay',
Farstream.CandidateType.MULTICAST: 'multicast'} Farstream.CandidateType.MULTICAST: 'multicast'
}
attrs = { attrs = {
'component': candidate.component_id, 'component': candidate.component_id,
'foundation': '1', # hack 'foundation': '1', # hack
@ -402,23 +418,26 @@ class JingleTransportICEUDP(JingleTransport):
# jingle # jingle
proto = Farstream.NetworkProtocol.TCP proto = Farstream.NetworkProtocol.TCP
priority = int(candidate['priority']) priority = int(candidate['priority'])
types = {'host': Farstream.CandidateType.HOST, types = {
'host': Farstream.CandidateType.HOST,
'srflx': Farstream.CandidateType.SRFLX, 'srflx': Farstream.CandidateType.SRFLX,
'prflx': Farstream.CandidateType.PRFLX, 'prflx': Farstream.CandidateType.PRFLX,
'relay': Farstream.CandidateType.RELAY, 'relay': Farstream.CandidateType.RELAY,
'multicast': Farstream.CandidateType.MULTICAST} 'multicast': Farstream.CandidateType.MULTICAST
}
if 'type' in candidate and candidate['type'] in types: if 'type' in candidate and candidate['type'] in types:
type_ = types[candidate['type']] type_ = types[candidate['type']]
else: else:
log.warning('Unknown type %s' % candidate['type']) log.warning('Unknown type %s', candidate['type'])
type_ = Farstream.CandidateType.HOST type_ = Farstream.CandidateType.HOST
username = str(transport['ufrag']) username = str(transport['ufrag'])
password = str(transport['pwd']) password = str(transport['pwd'])
ttl = 0 ttl = 0
cand = Farstream.Candidate.new_full(foundation, component_id, ip, cand = Farstream.Candidate.new_full(foundation, component_id, ip,
port, base_ip, base_port, proto, priority, type_, username, port, base_ip, base_port,
password, ttl) proto, priority, type_,
username, password, ttl)
candidates.append(cand) candidates.append(cand)
self.remote_candidates.extend(candidates) self.remote_candidates.extend(candidates)

View File

@ -16,13 +16,15 @@
## along with Gajim. If not, see <http://www.gnu.org/licenses/>. ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
## ##
import os
import nbxmpp
import logging import logging
import os
import nbxmpp
from common import gajim from common import gajim
log = logging.getLogger('gajim.c.jingle_xtls') log = logging.getLogger('gajim.c.jingle_xtls')
PYOPENSSL_PRESENT = False PYOPENSSL_PRESENT = False
# key-exchange id -> [callback, args], accept that session once key-exchange completes # key-exchange id -> [callback, args], accept that session once key-exchange completes
@ -53,7 +55,7 @@ DH_PARAMS = 'dh_params.pem'
DEFAULT_DH_PARAMS = 'dh4096.pem' DEFAULT_DH_PARAMS = 'dh4096.pem'
def default_callback(connection, certificate, error_num, depth, return_code): def default_callback(connection, certificate, error_num, depth, return_code):
log.info("certificate: %s" % certificate) log.info("certificate: %s", certificate)
return return_code return return_code
def load_cert_file(cert_path, cert_store=None): def load_cert_file(cert_path, cert_store=None):
@ -65,8 +67,8 @@ def load_cert_file(cert_path, cert_store=None):
try: try:
f = open(cert_path) f = open(cert_path)
except IOError as e: except IOError as e:
log.warning('Unable to open certificate file %s: %s' % (cert_path, log.warning('Unable to open certificate file %s: %s', cert_path,
str(e))) str(e))
return None return None
lines = f.readlines() lines = f.readlines()
i = 0 i = 0
@ -84,11 +86,11 @@ def load_cert_file(cert_path, cert_store=None):
f.close() f.close()
return x509cert return x509cert
except OpenSSL.crypto.Error as exception_obj: except OpenSSL.crypto.Error as exception_obj:
log.warning('Unable to load a certificate from file %s: %s' %\ log.warning('Unable to load a certificate from file %s: %s',
(cert_path, exception_obj.args[0][0][2])) cert_path, exception_obj.args[0][0][2])
except: except:
log.warning('Unknown error while loading certificate from file ' log.warning('Unknown error while loading certificate from file '
'%s' % cert_path) '%s', cert_path)
begin = -1 begin = -1
i += 1 i += 1
f.close() f.close()
@ -126,8 +128,8 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
with open(default_dh_params_name, "r") as default_dh_params_file: with open(default_dh_params_name, "r") as default_dh_params_file:
ctx.load_tmp_dh(default_dh_params_name.encode('utf-8')) ctx.load_tmp_dh(default_dh_params_name.encode('utf-8'))
except FileNotFoundError as err: except FileNotFoundError as err:
log.error('Unable to load default DH parameter file: %s , %s' log.error('Unable to load default DH parameter file: %s, %s',
% (default_dh_params_name, err)) default_dh_params_name, err)
raise raise
if remote_jid: if remote_jid:
@ -136,8 +138,8 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
remote_jid) + '.cert' remote_jid) + '.cert'
if os.path.exists(path): if os.path.exists(path):
load_cert_file(path, cert_store=store) load_cert_file(path, cert_store=store)
log.debug('certificate file ' + path + ' loaded fingerprint ' + \ log.debug('certificate file %s loaded fingerprint %s',
fingerprint) path, fingerprint)
return ctx return ctx
def read_cert(certpath): def read_cert(certpath):
@ -212,16 +214,16 @@ def send_cert_request(con, to_jid):
# the following code is partly due to pyopenssl examples # the following code is partly due to pyopenssl examples
def createKeyPair(type, bits): def createKeyPair(type_, bits):
""" """
Create a public/private key pair. Create a public/private key pair.
Arguments: type - Key type, must be one of TYPE_RSA and TYPE_DSA Arguments: type_ - Key type, must be one of TYPE_RSA and TYPE_DSA
bits - Number of bits to use in the key bits - Number of bits to use in the key
Returns: The public/private key pair in a PKey object Returns: The public/private key pair in a PKey object
""" """
pkey = crypto.PKey() pkey = crypto.PKey()
pkey.generate_key(type, bits) pkey.generate_key(type_, bits)
return pkey return pkey
def createCertRequest(pkey, digest="sha256", **name): def createCertRequest(pkey, digest="sha256", **name):