Fix a bunch of issues in common.jingle*.
This commit is contained in:
parent
57fb80f1fa
commit
ad9370afa8
|
@ -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')
|
||||||
|
|
|
@ -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():
|
||||||
|
@ -102,14 +104,14 @@ class ConnectionJingle(object):
|
||||||
if sid not in self._sessions:
|
if sid not in self._sessions:
|
||||||
#TODO: tie-breaking and other things...
|
#TODO: tie-breaking and other things...
|
||||||
newjingle = JingleSession(con=self, weinitiate=False, jid=jid,
|
newjingle = JingleSession(con=self, weinitiate=False, jid=jid,
|
||||||
iq_id=id_, sid=sid)
|
iq_id=id_, sid=sid)
|
||||||
self._sessions[sid] = newjingle
|
self._sessions[sid] = newjingle
|
||||||
# we already have such session in dispatcher...
|
# we already have such session in dispatcher...
|
||||||
self._sessions[sid].collect_iq_id(id_)
|
self._sessions[sid].collect_iq_id(id_)
|
||||||
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
|
||||||
|
|
||||||
|
@ -132,20 +134,20 @@ class ConnectionJingle(object):
|
||||||
jingle = self.get_jingle_session(jid, media='audio')
|
jingle = self.get_jingle_session(jid, media='audio')
|
||||||
if jingle:
|
if jingle:
|
||||||
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
|
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
|
||||||
out_xid=out_xid))
|
out_xid=out_xid))
|
||||||
else:
|
else:
|
||||||
jingle = JingleSession(self, weinitiate=True, jid=jid)
|
jingle = JingleSession(self, weinitiate=True, jid=jid)
|
||||||
self._sessions[jingle.sid] = jingle
|
self._sessions[jingle.sid] = jingle
|
||||||
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
|
jingle.add_content('video', JingleVideo(jingle, in_xid=in_xid,
|
||||||
out_xid=out_xid))
|
out_xid=out_xid))
|
||||||
jingle.start_session()
|
jingle.start_session()
|
||||||
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):
|
||||||
gcc = jid.split('/')
|
gcc = jid.split('/')
|
||||||
if len(gcc) == 2:
|
if len(gcc) == 2:
|
||||||
contact = gajim.contacts.get_gc_contact(self.name, gcc[0], gcc[1])
|
contact = gajim.contacts.get_gc_contact(self.name, gcc[0], gcc[1])
|
||||||
|
@ -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()
|
||||||
|
|
|
@ -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,35 +59,39 @@ 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,
|
||||||
self.__on_content_accept],
|
self.__on_content_accept],
|
||||||
'content-add': [self.__on_transport_info],
|
'content-add': [self.__on_transport_info],
|
||||||
'content-modify': [],
|
'content-modify': [],
|
||||||
'content-reject': [],
|
'content-reject': [],
|
||||||
'content-remove': [],
|
'content-remove': [],
|
||||||
'description-info': [],
|
'description-info': [],
|
||||||
'security-info': [],
|
'security-info': [],
|
||||||
'session-accept': [self.__on_transport_info,
|
'session-accept': [self.__on_transport_info,
|
||||||
self.__on_content_accept],
|
self.__on_content_accept],
|
||||||
'session-info': [],
|
'session-info': [],
|
||||||
'session-initiate': [self.__on_transport_info],
|
'session-initiate': [self.__on_transport_info],
|
||||||
'session-terminate': [],
|
'session-terminate': [],
|
||||||
'transport-info': [self.__on_transport_info],
|
'transport-info': [self.__on_transport_info],
|
||||||
'transport-replace': [self.__on_transport_replace],
|
'transport-replace': [self.__on_transport_replace],
|
||||||
'transport-accept': [],
|
'transport-accept': [],
|
||||||
'transport-reject': [],
|
'transport-reject': [],
|
||||||
'iq-result': [],
|
'iq-result': [],
|
||||||
'iq-error': [],
|
'iq-error': [],
|
||||||
# these are called when *we* sent these stanzas
|
# these are called when *we* sent these stanzas
|
||||||
'content-accept-sent': [self.__fill_jingle_stanza,
|
'content-accept-sent': [self.__fill_jingle_stanza,
|
||||||
self.__on_content_accept],
|
self.__on_content_accept],
|
||||||
'content-add-sent': [self.__fill_jingle_stanza],
|
'content-add-sent': [self.__fill_jingle_stanza],
|
||||||
'session-initiate-sent': [self.__fill_jingle_stanza],
|
'session-initiate-sent': [self.__fill_jingle_stanza],
|
||||||
'session-accept-sent': [self.__fill_jingle_stanza,
|
'session-accept-sent': [self.__fill_jingle_stanza,
|
||||||
self.__on_content_accept],
|
self.__on_content_accept],
|
||||||
'session-terminate-sent': [],
|
'session-terminate-sent': [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def is_ready(self):
|
def is_ready(self):
|
||||||
|
@ -134,8 +138,8 @@ class JingleContent(object):
|
||||||
if payload is None:
|
if payload is None:
|
||||||
payload = []
|
payload = []
|
||||||
return nbxmpp.Node('content',
|
return nbxmpp.Node('content',
|
||||||
attrs={'name': self.name, 'creator': self.creator},
|
attrs={'name': self.name, 'creator': self.creator},
|
||||||
payload=payload)
|
payload=payload)
|
||||||
|
|
||||||
def send_candidate(self, candidate):
|
def send_candidate(self, candidate):
|
||||||
"""
|
"""
|
||||||
|
@ -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)]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,41 +19,47 @@
|
||||||
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):
|
||||||
# We send the candidates and we are waiting for a reply
|
NOT_STARTED = 0
|
||||||
STATE_CAND_SENT = 2
|
INITIALIZED = 1
|
||||||
# We received the candidates and we are waiting to reply
|
# We send the candidates and we are waiting for a reply
|
||||||
STATE_CAND_RECEIVED = 3
|
CAND_SENT = 2
|
||||||
# We have sent and received the candidates
|
# We received the candidates and we are waiting to reply
|
||||||
# This also includes any candidate-error received or sent
|
CAND_RECEIVED = 3
|
||||||
STATE_CAND_SENT_AND_RECEIVED = 4
|
# We have sent and received the candidates
|
||||||
STATE_TRANSPORT_REPLACE = 5
|
# This also includes any candidate-error received or sent
|
||||||
# We are transfering the file
|
CAND_SENT_AND_RECEIVED = 4
|
||||||
STATE_TRANSFERING = 6
|
TRANSPORT_REPLACE = 5
|
||||||
|
# We are transfering the file
|
||||||
|
TRANSFERING = 6
|
||||||
|
|
||||||
|
|
||||||
class JingleFileTransfer(JingleContent):
|
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,29 +91,31 @@ 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 = {}
|
||||||
if gajim.contacts.is_gc_contact(session.connection.name,
|
if gajim.contacts.is_gc_contact(session.connection.name,
|
||||||
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,8 +137,10 @@ 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,
|
||||||
FT_content=self))
|
stanza=stanza,
|
||||||
|
jingle_content=content,
|
||||||
|
FT_content=self))
|
||||||
if self.session.request:
|
if self.session.request:
|
||||||
# accept the request
|
# accept the request
|
||||||
self.session.approve_content(self.media, self.name)
|
self.session.approve_content(self.media, self.name)
|
||||||
|
@ -141,10 +151,11 @@ 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)
|
||||||
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,
|
||||||
|
@ -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()
|
||||||
|
@ -192,30 +203,31 @@ class JingleFileTransfer(JingleContent):
|
||||||
fingerprint = fingerprint.getData()
|
fingerprint = fingerprint.getData()
|
||||||
self.x509_fingerprint = fingerprint
|
self.x509_fingerprint = fingerprint
|
||||||
if not jingle_xtls.check_cert(gajim.get_jid_without_resource(
|
if not jingle_xtls.check_cert(gajim.get_jid_without_resource(
|
||||||
self.session.responder), fingerprint):
|
self.session.responder), fingerprint):
|
||||||
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,
|
||||||
receiving=False)
|
self._on_connect_error,
|
||||||
|
fingerprint=fingerprint,
|
||||||
|
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
|
||||||
|
@ -356,44 +370,40 @@ class JingleFileTransfer(JingleContent):
|
||||||
receiver = self.file_props.receiver
|
receiver = self.file_props.receiver
|
||||||
sender = self.file_props.sender
|
sender = self.file_props.sender
|
||||||
sha_str = helpers.get_auth_sha(self.file_props.sid, sender,
|
sha_str = helpers.get_auth_sha(self.file_props.sid, sender,
|
||||||
receiver)
|
receiver)
|
||||||
self.file_props.sha_str = sha_str
|
self.file_props.sha_str = sha_str
|
||||||
port = gajim.config.get('file_transfers_port')
|
port = gajim.config.get('file_transfers_port')
|
||||||
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._store_socks5_sid, self.file_props,
|
self.file_props,
|
||||||
fingerprint=fingerprint, typ='sender')
|
fingerprint=fingerprint,
|
||||||
else:
|
typ='sender' if self.weinitiate else 'receiver')
|
||||||
listener = gajim.socks5queue.start_listener(port, sha_str,
|
|
||||||
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):
|
||||||
|
|
|
@ -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,16 +85,16 @@ 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):
|
||||||
'''
|
'''
|
||||||
This state happens when we receive a candidate.
|
This state happens when we receive a candidate.
|
||||||
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,17 +105,17 @@ 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):
|
||||||
'''
|
'''
|
||||||
This state happens when we have received and sent the candidates.
|
This state happens when we have received and sent the candidates.
|
||||||
It takes the boolean argument: sendCand in order to decide whether
|
It takes the boolean argument: sendCand in order to decide whether
|
||||||
|
@ -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)
|
||||||
|
@ -191,34 +196,34 @@ class StateTransfering(JingleFileTransferStates):
|
||||||
gajim.socks5queue.idx += 1
|
gajim.socks5queue.idx += 1
|
||||||
idx = gajim.socks5queue.idx
|
idx = gajim.socks5queue.idx
|
||||||
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,
|
||||||
sockobj)
|
sockobj)
|
||||||
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
|
||||||
else:
|
else:
|
||||||
jid = gajim.get_jid_without_resource(self.jft.session.ourjid)
|
jid = gajim.get_jid_without_resource(self.jft.session.ourjid)
|
||||||
gajim.socks5queue.send_file(self.jft.file_props,
|
gajim.socks5queue.send_file(self.jft.file_props,
|
||||||
self.jft.session.connection.name, mode)
|
self.jft.session.connection.name, mode)
|
||||||
|
|
||||||
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()
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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]
|
||||||
|
@ -90,32 +97,32 @@ class JingleRTPContent(JingleContent):
|
||||||
if stun_server:
|
if stun_server:
|
||||||
try:
|
try:
|
||||||
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
|
||||||
|
|
||||||
self.p2pstream = self.p2psession.new_stream(participant,
|
self.p2pstream = self.p2psession.new_stream(participant,
|
||||||
Farstream.StreamDirection.BOTH)
|
Farstream.StreamDirection.BOTH)
|
||||||
self.p2pstream.connect('src-pad-added', on_src_pad_added)
|
self.p2pstream.connect('src-pad-added', on_src_pad_added)
|
||||||
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(
|
||||||
pri_txt=_('%s configuration error') % text.capitalize(),
|
None, conn=self.session.connection, level='error',
|
||||||
sec_txt=_("Couldn't setup %s. Check your configuration.\n\n"
|
pri_txt=_('%s configuration error') % text.capitalize(),
|
||||||
"Pipeline was:\n%s\n\nError was:\n%s") % (text, pipeline,
|
sec_txt=_('Couldn’t setup %s. Check your configuration.\n\n'
|
||||||
str(e))))
|
'Pipeline was:\n%s\n\nError was:\n%s') % (text, pipeline, str(e))))
|
||||||
raise JingleContentSetupException
|
raise JingleContentSetupException
|
||||||
|
|
||||||
def add_remote_candidates(self, candidates):
|
def add_remote_candidates(self, candidates):
|
||||||
|
@ -147,7 +154,7 @@ class JingleRTPContent(JingleContent):
|
||||||
def _start_dtmf(self, event):
|
def _start_dtmf(self, event):
|
||||||
if event in ('*', '#'):
|
if event in ('*', '#'):
|
||||||
event = {'*': Farstream.DTMFEvent.STAR,
|
event = {'*': Farstream.DTMFEvent.STAR,
|
||||||
'#': Farstream.DTMFEvent.POUND}[event]
|
'#': Farstream.DTMFEvent.POUND}[event]
|
||||||
else:
|
else:
|
||||||
event = int(event)
|
event = int(event)
|
||||||
self.p2psession.start_telephony_event(event, 2)
|
self.p2psession.start_telephony_event(event, 2)
|
||||||
|
@ -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':
|
||||||
|
@ -189,7 +197,7 @@ class JingleRTPContent(JingleContent):
|
||||||
self.transport.remote_candidates)
|
self.transport.remote_candidates)
|
||||||
self.transport.remote_candidates = []
|
self.transport.remote_candidates = []
|
||||||
self.p2pstream.set_property('direction',
|
self.p2pstream.set_property('direction',
|
||||||
Farstream.StreamDirection.BOTH)
|
Farstream.StreamDirection.BOTH)
|
||||||
|
|
||||||
elif name == 'farstream-local-candidates-prepared':
|
elif name == 'farstream-local-candidates-prepared':
|
||||||
self.candidates_ready = True
|
self.candidates_ready = True
|
||||||
|
@ -208,19 +216,21 @@ 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'),
|
||||||
message.get_structure().get_value('debug')))))
|
sec_txt=_('Error: %s\nDebug: %s' %
|
||||||
|
(message.get_structure().get_value('gerror'),
|
||||||
|
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):
|
||||||
|
@ -270,7 +281,7 @@ class JingleRTPContent(JingleContent):
|
||||||
# ignore invalid payload-types
|
# ignore invalid payload-types
|
||||||
continue
|
continue
|
||||||
c = Farstream.Codec.new(int(codec['id']), codec['name'],
|
c = Farstream.Codec.new(int(codec['id']), codec['name'],
|
||||||
self.farstream_media, int(codec['clockrate']))
|
self.farstream_media, int(codec['clockrate']))
|
||||||
if 'channels' in codec:
|
if 'channels' in codec:
|
||||||
c.channels = int(codec['channels'])
|
c.channels = int(codec['channels'])
|
||||||
else:
|
else:
|
||||||
|
@ -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,19 +357,22 @@ 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.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, 16000),
|
||||||
|
Farstream.Codec.new(Farstream.CODEC_ID_ANY, 'SPEEX',
|
||||||
|
Farstream.MediaType.AUDIO, 8000)]
|
||||||
self.p2psession.set_codec_preferences(codecs)
|
self.p2psession.set_codec_preferences(codecs)
|
||||||
|
|
||||||
# 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,15 +428,16 @@ 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)
|
||||||
|
|
||||||
self.sink = self.make_bin_from_config('video_output_device',
|
self.sink = self.make_bin_from_config('video_output_device',
|
||||||
'videoscale ! videoconvert ! %s',
|
'videoscale ! videoconvert ! %s',
|
||||||
_("video output"))
|
_("video output"))
|
||||||
|
|
||||||
self.pipeline.add(self.sink)
|
self.pipeline.add(self.sink)
|
||||||
|
|
||||||
|
|
|
@ -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,17 +65,18 @@ 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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, con, weinitiate, jid, iq_id=None, sid=None,
|
def __init__(self, con, weinitiate, jid, iq_id=None, sid=None,
|
||||||
werequest=False):
|
werequest=False):
|
||||||
"""
|
"""
|
||||||
con -- connection object,
|
con -- connection object,
|
||||||
weinitiate -- boolean, are we the initiator?
|
weinitiate -- boolean, are we the initiator?
|
||||||
|
@ -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,37 +111,37 @@ 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
|
||||||
self.callbacks = {
|
self.callbacks = {
|
||||||
'content-accept': [self.__ack, self.__on_content_accept,
|
'content-accept': [self.__ack, self.__on_content_accept,
|
||||||
self.__broadcast],
|
self.__broadcast],
|
||||||
'content-add': [self.__ack,
|
'content-add': [self.__ack,
|
||||||
self.__on_content_add, self.__broadcast
|
self.__on_content_add, self.__broadcast
|
||||||
], #TODO
|
], #TODO
|
||||||
'content-modify': [self.__ack], #TODO
|
'content-modify': [self.__ack], #TODO
|
||||||
'content-reject': [self.__ack, self.__on_content_remove],
|
'content-reject': [self.__ack, self.__on_content_remove],
|
||||||
'content-remove': [self.__ack, self.__on_content_remove],
|
'content-remove': [self.__ack, self.__on_content_remove],
|
||||||
'description-info': [self.__ack, self.__broadcast], #TODO
|
'description-info': [self.__ack, self.__broadcast], #TODO
|
||||||
'security-info': [self.__ack], #TODO
|
'security-info': [self.__ack], #TODO
|
||||||
'session-accept': [self.__ack, self.__on_session_accept,
|
'session-accept': [self.__ack, self.__on_session_accept,
|
||||||
self.__on_content_accept,
|
self.__on_content_accept,
|
||||||
self.__broadcast],
|
self.__broadcast],
|
||||||
'session-info': [self.__ack, self.__broadcast,
|
'session-info': [self.__ack, self.__broadcast,
|
||||||
self.__on_session_info ],
|
self.__on_session_info],
|
||||||
'session-initiate': [self.__ack, self.__on_session_initiate,
|
'session-initiate': [self.__ack, self.__on_session_initiate,
|
||||||
self.__broadcast],
|
self.__broadcast],
|
||||||
'session-terminate': [self.__ack,self.__on_session_terminate,
|
'session-terminate': [self.__ack, self.__on_session_terminate,
|
||||||
self.__broadcast_all],
|
self.__broadcast_all],
|
||||||
'transport-info': [self.__ack, self.__broadcast],
|
'transport-info': [self.__ack, self.__broadcast],
|
||||||
'transport-replace': [self.__ack, self.__broadcast,
|
'transport-replace': [self.__ack, self.__broadcast,
|
||||||
self.__on_transport_replace], #TODO
|
self.__on_transport_replace], #TODO
|
||||||
'transport-accept': [self.__ack], #TODO
|
'transport-accept': [self.__ack], #TODO
|
||||||
'transport-reject': [self.__ack], #TODO
|
'transport-reject': [self.__ack], #TODO
|
||||||
'iq-result': [self.__broadcast],
|
'iq-result': [self.__broadcast],
|
||||||
'iq-error': [self.__on_error],
|
'iq-error': [self.__on_error],
|
||||||
}
|
}
|
||||||
|
|
||||||
def collect_iq_id(self, iq_id):
|
def collect_iq_id(self, iq_id):
|
||||||
|
@ -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')
|
||||||
|
@ -232,11 +237,11 @@ class JingleSession(object):
|
||||||
if not self.contents:
|
if not self.contents:
|
||||||
self.end_session()
|
self.end_session()
|
||||||
|
|
||||||
def modify_content(self, creator, name, transport = None):
|
def modify_content(self, creator, name, transport=None):
|
||||||
'''
|
'''
|
||||||
Currently used for transport replacement
|
Currently used for transport replacement
|
||||||
'''
|
'''
|
||||||
content = self.contents[(creator,name)]
|
content = self.contents[(creator, name)]
|
||||||
transport.set_sid(content.transport.sid)
|
transport.set_sid(content.transport.sid)
|
||||||
transport.set_file_props(content.transport.file_props)
|
transport.set_file_props(content.transport.file_props)
|
||||||
content.transport = transport
|
content.transport = transport
|
||||||
|
@ -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:
|
||||||
|
@ -278,7 +283,7 @@ class JingleSession(object):
|
||||||
Return True when all codecs and candidates are ready (for all contents)
|
Return True when all codecs and candidates are ready (for all contents)
|
||||||
"""
|
"""
|
||||||
return (any((content.is_ready() for content in self.contents.values()))
|
return (any((content.is_ready() for content in self.contents.values()))
|
||||||
and self.accepted)
|
and self.accepted)
|
||||||
|
|
||||||
def accept_session(self):
|
def accept_session(self):
|
||||||
"""
|
"""
|
||||||
|
@ -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)
|
||||||
|
@ -334,8 +339,8 @@ class JingleSession(object):
|
||||||
self.__send_error(stanza, 'bad-request')
|
self.__send_error(stanza, 'bad-request')
|
||||||
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,15 +410,15 @@ 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()
|
||||||
else:
|
else:
|
||||||
stanza, jingle = self.__make_jingle('transport-reject')
|
stanza, jingle = self.__make_jingle('transport-reject')
|
||||||
content = jingle.setTag('content', attrs={'creator': creator,
|
content = jingle.setTag('content', attrs={'creator': creator,
|
||||||
'name': name})
|
'name': name})
|
||||||
content.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
|
||||||
|
@ -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,12 +438,12 @@ 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:
|
||||||
file_props = FilesProp.getFileProp(self.connection.name,
|
file_props = FilesProp.getFileProp(self.connection.name,
|
||||||
|
@ -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,10 +603,12 @@ 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,
|
||||||
reason=text))
|
jingle_session=self,
|
||||||
|
media=None,
|
||||||
|
reason=text))
|
||||||
|
|
||||||
def __broadcast_all(self, stanza, jingle, error, action):
|
def __broadcast_all(self, stanza, jingle, error, action):
|
||||||
"""
|
"""
|
||||||
|
@ -621,7 +632,7 @@ class JingleSession(object):
|
||||||
if transport:
|
if transport:
|
||||||
content = content_type(self, transport)
|
content = content_type(self, transport)
|
||||||
self.add_content(element['name'],
|
self.add_content(element['name'],
|
||||||
content, 'peer')
|
content, 'peer')
|
||||||
contents.append((content.media,))
|
contents.append((content.media,))
|
||||||
else:
|
else:
|
||||||
reasons.add('unsupported-transports')
|
reasons.add('unsupported-transports')
|
||||||
|
@ -634,7 +645,7 @@ class JingleSession(object):
|
||||||
failure_reason = None
|
failure_reason = None
|
||||||
# Store the first reason of failure
|
# Store the first reason of failure
|
||||||
for reason in ('failed-application', 'unsupported-transports',
|
for reason in ('failed-application', 'unsupported-transports',
|
||||||
'unsupported-applications'):
|
'unsupported-applications'):
|
||||||
if reason in reasons:
|
if reason in reasons:
|
||||||
failure_reason = reason
|
failure_reason = reason
|
||||||
break
|
break
|
||||||
|
@ -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,
|
||||||
reason=text or error))
|
jingle_session=self,
|
||||||
|
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',
|
||||||
'general-error', 'gone', 'incompatible-parameters', 'media-error',
|
'expired', 'failed-application', 'failed-transport',
|
||||||
'security-error', 'timeout', 'unsupported-applications',
|
'general-error', 'gone', 'incompatible-parameters', 'media-error',
|
||||||
'unsupported-transports']
|
'security-error', 'timeout', 'unsupported-applications',
|
||||||
|
'unsupported-transports'
|
||||||
|
]
|
||||||
tag = stanza.getTag('reason')
|
tag = stanza.getTag('reason')
|
||||||
text = ''
|
text = ''
|
||||||
if tag:
|
if tag:
|
||||||
|
@ -668,12 +683,14 @@ 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 = {
|
||||||
'sid': self.sid,
|
'action': action,
|
||||||
'initiator' : self.initiator}
|
'sid': self.sid,
|
||||||
|
'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:
|
||||||
jingle.addChild(node=reason)
|
jingle.addChild(node=reason)
|
||||||
return stanza, jingle
|
return stanza, jingle
|
||||||
|
@ -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,
|
||||||
reason=text))
|
jingle_session=self,
|
||||||
|
media=None,
|
||||||
|
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,
|
||||||
reason='rejected'))
|
jingle_session=self,
|
||||||
|
media=content.media,
|
||||||
|
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,
|
||||||
reason='removed'))
|
jingle_session=self,
|
||||||
|
media=content.media,
|
||||||
|
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))
|
||||||
|
|
|
@ -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'],
|
||||||
|
@ -124,8 +130,8 @@ class JingleTransportSocks5(JingleTransport):
|
||||||
|
|
||||||
return nbxmpp.Node('candidate', attrs=attrs)
|
return nbxmpp.Node('candidate', attrs=attrs)
|
||||||
|
|
||||||
def make_transport(self, candidates=None, add_candidates = True):
|
def make_transport(self, candidates=None, add_candidates=True):
|
||||||
if add_candidates:
|
if add_candidates:
|
||||||
self._add_local_ips_as_candidates()
|
self._add_local_ips_as_candidates()
|
||||||
self._add_additional_candidates()
|
self._add_additional_candidates()
|
||||||
self._add_proxy_candidates()
|
self._add_proxy_candidates()
|
||||||
|
@ -174,7 +180,7 @@ class JingleTransportSocks5(JingleTransport):
|
||||||
|
|
||||||
def _add_local_ips_as_candidates(self):
|
def _add_local_ips_as_candidates(self):
|
||||||
if not gajim.config.get_per('accounts', self.connection.name,
|
if not gajim.config.get_per('accounts', self.connection.name,
|
||||||
'ft_send_local_ips'):
|
'ft_send_local_ips'):
|
||||||
return
|
return
|
||||||
if not self.connection:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
|
@ -186,30 +192,34 @@ class JingleTransportSocks5(JingleTransport):
|
||||||
hosts = set()
|
hosts = set()
|
||||||
local_ip_cand = []
|
local_ip_cand = []
|
||||||
|
|
||||||
c = {'host': self.connection.peerhost[0],
|
candidate = {
|
||||||
'candidate_id': self.connection.connection.getAnID(),
|
'host': self.connection.peerhost[0],
|
||||||
'port': port,
|
'candidate_id': self.connection.connection.getAnID(),
|
||||||
'type': 'direct',
|
'port': port,
|
||||||
'jid': self.ourjid,
|
'type': 'direct',
|
||||||
'priority': priority}
|
'jid': self.ourjid,
|
||||||
|
'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 = {
|
||||||
'candidate_id': self.connection.connection.getAnID(),
|
'host': addr,
|
||||||
'port': port,
|
'candidate_id': self.connection.connection.getAnID(),
|
||||||
'type': 'direct',
|
'port': port,
|
||||||
'jid': self.ourjid,
|
'type': 'direct',
|
||||||
'priority': priority,
|
'jid': self.ourjid,
|
||||||
'initiator': self.file_props.sender,
|
'priority': priority,
|
||||||
'target': self.file_props.receiver}
|
'initiator': self.file_props.sender,
|
||||||
|
'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 = {
|
||||||
'candidate_id': self.connection.connection.getAnID(),
|
'host': host,
|
||||||
'port': port,
|
'candidate_id': self.connection.connection.getAnID(),
|
||||||
'type': 'direct',
|
'port': port,
|
||||||
'jid': self.ourjid,
|
'type': 'direct',
|
||||||
'priority': priority,
|
'jid': self.ourjid,
|
||||||
'initiator': self.file_props.sender,
|
'priority': priority,
|
||||||
'target': self.file_props.receiver}
|
'initiator': self.file_props.sender,
|
||||||
additional_ip_cand.append(c)
|
'target': self.file_props.receiver
|
||||||
|
}
|
||||||
|
additional_ip_cand.append(candidate)
|
||||||
|
|
||||||
self._add_candidates(additional_ip_cand)
|
self._add_candidates(additional_ip_cand)
|
||||||
|
|
||||||
|
@ -252,21 +264,23 @@ 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 = {
|
||||||
'candidate_id': self.connection.connection.getAnID(),
|
'host': proxyhost['host'],
|
||||||
'port': int(proxyhost['port']),
|
'candidate_id': self.connection.connection.getAnID(),
|
||||||
'type': 'proxy',
|
'port': int(proxyhost['port']),
|
||||||
'jid': proxyhost['jid'],
|
'type': 'proxy',
|
||||||
'priority': priority,
|
'jid': proxyhost['jid'],
|
||||||
'initiator': self.file_props.sender,
|
'priority': priority,
|
||||||
'target': self.file_props.receiver}
|
'initiator': self.file_props.sender,
|
||||||
proxy_cand.append(c)
|
'target': self.file_props.receiver
|
||||||
|
}
|
||||||
|
proxy_cand.append(candidate)
|
||||||
|
|
||||||
self._add_candidates(proxy_cand)
|
self._add_candidates(proxy_cand)
|
||||||
|
|
||||||
def get_content(self):
|
def get_content(self):
|
||||||
sesn = self.connection.get_jingle_session(self.ourjid,
|
sesn = self.connection.get_jingle_session(self.ourjid,
|
||||||
self.file_props.sid)
|
self.file_props.sid)
|
||||||
for content in sesn.contents.values():
|
for content in sesn.contents.values():
|
||||||
if content.transport == self:
|
if content.transport == self:
|
||||||
return content
|
return content
|
||||||
|
@ -277,7 +291,7 @@ class JingleTransportSocks5(JingleTransport):
|
||||||
if not self.connection:
|
if not self.connection:
|
||||||
return
|
return
|
||||||
sesn = self.connection.get_jingle_session(self.ourjid,
|
sesn = self.connection.get_jingle_session(self.ourjid,
|
||||||
self.file_props.sid)
|
self.file_props.sid)
|
||||||
if sesn is None:
|
if sesn is None:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@ -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'])
|
||||||
|
@ -307,7 +321,7 @@ class JingleTransportSocks5(JingleTransport):
|
||||||
else:
|
else:
|
||||||
for host in self.candidates:
|
for host in self.candidates:
|
||||||
if host['host'] == proxy['host'] and host['jid'] == proxy['jid'] \
|
if host['host'] == proxy['host'] and host['jid'] == proxy['jid'] \
|
||||||
and host['port'] == proxy['port']:
|
and host['port'] == proxy['port']:
|
||||||
cid = host['candidate_id']
|
cid = host['candidate_id']
|
||||||
break
|
break
|
||||||
if cid is None:
|
if cid is None:
|
||||||
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
@ -105,7 +107,7 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
|
||||||
|
|
||||||
if fingerprint == 'server': # for testing purposes only
|
if fingerprint == 'server': # for testing purposes only
|
||||||
ctx.set_verify(SSL.VERIFY_NONE|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
ctx.set_verify(SSL.VERIFY_NONE|SSL.VERIFY_FAIL_IF_NO_PEER_CERT,
|
||||||
verify_cb or default_callback)
|
verify_cb or default_callback)
|
||||||
elif fingerprint == 'client':
|
elif fingerprint == 'client':
|
||||||
ctx.set_verify(SSL.VERIFY_PEER, verify_cb or default_callback)
|
ctx.set_verify(SSL.VERIFY_PEER, verify_cb or default_callback)
|
||||||
|
|
||||||
|
@ -121,23 +123,23 @@ def get_context(fingerprint, verify_cb=None, remote_jid=None):
|
||||||
ctx.load_tmp_dh(dh_params_name.encode('utf-8'))
|
ctx.load_tmp_dh(dh_params_name.encode('utf-8'))
|
||||||
except FileNotFoundError as err:
|
except FileNotFoundError as err:
|
||||||
default_dh_params_name = os.path.join(gajim.DATA_DIR,
|
default_dh_params_name = os.path.join(gajim.DATA_DIR,
|
||||||
'other', DEFAULT_DH_PARAMS)
|
'other', DEFAULT_DH_PARAMS)
|
||||||
try:
|
try:
|
||||||
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:
|
||||||
store = ctx.get_cert_store()
|
store = ctx.get_cert_store()
|
||||||
path = os.path.join(os.path.expanduser(gajim.MY_PEER_CERTS_PATH),
|
path = os.path.join(os.path.expanduser(gajim.MY_PEER_CERTS_PATH),
|
||||||
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):
|
||||||
|
@ -244,7 +246,7 @@ def createCertRequest(pkey, digest="sha256", **name):
|
||||||
req = crypto.X509Req()
|
req = crypto.X509Req()
|
||||||
subj = req.get_subject()
|
subj = req.get_subject()
|
||||||
|
|
||||||
for (key,value) in name.items():
|
for (key, value) in name.items():
|
||||||
setattr(subj, key, value)
|
setattr(subj, key, value)
|
||||||
|
|
||||||
req.set_pubkey(pkey)
|
req.set_pubkey(pkey)
|
||||||
|
|
Loading…
Reference in New Issue