2007-08-06 15:53:01 +00:00
|
|
|
##
|
|
|
|
## Copyright (C) 2006 Gajim Team
|
|
|
|
##
|
|
|
|
## This program is free software; you can redistribute it and/or modify
|
|
|
|
## it under the terms of the GNU General Public License as published
|
|
|
|
## by the Free Software Foundation; version 2 only.
|
|
|
|
##
|
|
|
|
## This program is distributed in the hope that it will be useful,
|
|
|
|
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
## GNU General Public License for more details.
|
|
|
|
##
|
2009-11-26 13:58:12 +02:00
|
|
|
"""
|
|
|
|
Handles the jingle signalling protocol
|
|
|
|
"""
|
2007-08-06 15:53:01 +00:00
|
|
|
|
2009-09-13 11:02:49 +02:00
|
|
|
#TODO:
|
2009-09-16 13:55:54 +02:00
|
|
|
# * things in XEP 0176, including:
|
|
|
|
# - http://xmpp.org/extensions/xep-0176.html#protocol-restarts
|
|
|
|
# - http://xmpp.org/extensions/xep-0176.html#fallback
|
|
|
|
# * XEP 0177 (raw udp)
|
|
|
|
|
2009-09-13 11:02:49 +02:00
|
|
|
# * UI:
|
|
|
|
# - make state and codec informations available to the user
|
2009-09-18 20:17:35 +02:00
|
|
|
# - video integration
|
2009-09-13 11:02:49 +02:00
|
|
|
# * config:
|
|
|
|
# - codecs
|
2009-09-18 20:17:35 +02:00
|
|
|
|
2009-11-15 20:47:06 +01:00
|
|
|
# * figure out why it doesn't work with pidgin:
|
|
|
|
# That's maybe a bug in pidgin:
|
|
|
|
# http://xmpp.org/extensions/xep-0176.html#protocol-checks
|
2009-09-18 20:17:35 +02:00
|
|
|
|
2007-08-06 15:53:01 +00:00
|
|
|
import xmpp
|
2009-09-26 10:29:08 +02:00
|
|
|
import helpers
|
2007-08-06 15:53:01 +00:00
|
|
|
|
2010-03-21 21:45:45 +01:00
|
|
|
from jingle_session import JingleSession, JingleStates
|
2009-11-15 20:47:06 +01:00
|
|
|
from jingle_rtp import JingleAudio, JingleVideo
|
2010-06-06 23:22:52 +08:00
|
|
|
from jingle_ft import JingleFileTransfer
|
2009-09-13 11:02:49 +02:00
|
|
|
|
2010-06-06 23:22:52 +08:00
|
|
|
import logging
|
|
|
|
logger = logging.getLogger('gajim.c.jingle')
|
2009-09-17 23:36:26 +02:00
|
|
|
|
2007-08-06 15:53:01 +00:00
|
|
|
class ConnectionJingle(object):
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
This object depends on that it is a part of Connection class.
|
|
|
|
"""
|
|
|
|
|
|
|
|
def __init__(self):
|
2010-03-14 21:51:04 +01:00
|
|
|
# dictionary: sessionid => JingleSession object
|
2010-02-08 15:08:40 +01:00
|
|
|
self.__sessions = {}
|
|
|
|
|
|
|
|
# dictionary: (jid, iq stanza id) => JingleSession object,
|
|
|
|
# one time callbacks
|
|
|
|
self.__iq_responses = {}
|
|
|
|
|
2010-03-14 21:51:04 +01:00
|
|
|
def delete_jingle_session(self, sid):
|
2010-02-08 15:08:40 +01:00
|
|
|
"""
|
|
|
|
Remove a jingle session from a jingle stanza dispatcher
|
|
|
|
"""
|
2010-03-14 21:51:04 +01:00
|
|
|
if sid in self.__sessions:
|
2010-02-08 15:08:40 +01:00
|
|
|
#FIXME: Move this elsewhere?
|
2010-03-14 21:51:04 +01:00
|
|
|
for content in self.__sessions[sid].contents.values():
|
2010-02-08 15:08:40 +01:00
|
|
|
content.destroy()
|
2010-03-14 21:51:04 +01:00
|
|
|
self.__sessions[sid].callbacks = []
|
|
|
|
del self.__sessions[sid]
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
def _JingleCB(self, con, stanza):
|
|
|
|
"""
|
|
|
|
The jingle stanza dispatcher
|
|
|
|
|
|
|
|
Route jingle stanza to proper JingleSession object, or create one if it
|
|
|
|
is a new session.
|
|
|
|
|
|
|
|
TODO: Also check if the stanza isn't an error stanza, if so route it
|
|
|
|
adequatelly.
|
|
|
|
"""
|
|
|
|
# get data
|
|
|
|
jid = helpers.get_full_jid_from_iq(stanza)
|
|
|
|
id = stanza.getID()
|
|
|
|
|
|
|
|
if (jid, id) in self.__iq_responses.keys():
|
|
|
|
self.__iq_responses[(jid, id)].on_stanza(stanza)
|
|
|
|
del self.__iq_responses[(jid, id)]
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
jingle = stanza.getTag('jingle')
|
2010-07-03 16:22:47 +08:00
|
|
|
# a jingle element is not necessary in iq-result stanza
|
|
|
|
# don't check for that
|
|
|
|
if jingle:
|
|
|
|
sid = jingle.getAttr('sid')
|
|
|
|
else:
|
|
|
|
sid = None
|
|
|
|
for sesn in self.__sessions.values():
|
|
|
|
if id in sesn.iq_ids:
|
|
|
|
sesn.on_stanza(stanza)
|
|
|
|
return
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
# do we need to create a new jingle object
|
2010-03-14 21:51:04 +01:00
|
|
|
if sid not in self.__sessions:
|
2010-02-08 15:08:40 +01:00
|
|
|
#TODO: tie-breaking and other things...
|
2010-07-03 16:22:47 +08:00
|
|
|
newjingle = JingleSession(con=self, weinitiate=False, jid=jid,
|
|
|
|
iq_id = id, sid=sid)
|
2010-03-14 21:51:04 +01:00
|
|
|
self.__sessions[sid] = newjingle
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
# we already have such session in dispatcher...
|
2010-07-03 16:22:47 +08:00
|
|
|
self.__sessions[sid].collect_iq_id(id)
|
2010-03-14 21:51:04 +01:00
|
|
|
self.__sessions[sid].on_stanza(stanza)
|
2010-03-21 21:45:45 +01:00
|
|
|
# Delete invalid/unneeded sessions
|
|
|
|
if sid in self.__sessions and self.__sessions[sid].state == JingleStates.ended:
|
|
|
|
self.delete_jingle_session(sid)
|
2010-02-08 15:08:40 +01:00
|
|
|
|
|
|
|
raise xmpp.NodeProcessed
|
|
|
|
|
|
|
|
def start_audio(self, jid):
|
|
|
|
if self.get_jingle_session(jid, media='audio'):
|
|
|
|
return self.get_jingle_session(jid, media='audio').sid
|
|
|
|
jingle = self.get_jingle_session(jid, media='video')
|
|
|
|
if jingle:
|
|
|
|
jingle.add_content('voice', JingleAudio(jingle))
|
|
|
|
else:
|
|
|
|
jingle = JingleSession(self, weinitiate=True, jid=jid)
|
2010-03-14 21:51:04 +01:00
|
|
|
self.__sessions[jingle.sid] = jingle
|
2010-02-08 15:08:40 +01:00
|
|
|
jingle.add_content('voice', JingleAudio(jingle))
|
|
|
|
jingle.start_session()
|
|
|
|
return jingle.sid
|
|
|
|
|
|
|
|
def start_video(self, jid):
|
|
|
|
if self.get_jingle_session(jid, media='video'):
|
|
|
|
return self.get_jingle_session(jid, media='video').sid
|
|
|
|
jingle = self.get_jingle_session(jid, media='audio')
|
|
|
|
if jingle:
|
|
|
|
jingle.add_content('video', JingleVideo(jingle))
|
|
|
|
else:
|
|
|
|
jingle = JingleSession(self, weinitiate=True, jid=jid)
|
2010-03-14 21:51:04 +01:00
|
|
|
self.__sessions[jingle.sid] = jingle
|
2010-02-08 15:08:40 +01:00
|
|
|
jingle.add_content('video', JingleVideo(jingle))
|
|
|
|
jingle.start_session()
|
|
|
|
return jingle.sid
|
|
|
|
|
2010-06-06 23:22:52 +08:00
|
|
|
def start_file_transfer(self, jid, file_props):
|
2010-07-08 16:05:19 +08:00
|
|
|
logger.info("start file transfer with file: %s" % file_props)
|
2010-06-06 23:22:52 +08:00
|
|
|
jingle = self.get_jingle_session(jid, media='file')
|
|
|
|
if jingle:
|
2010-07-08 16:05:19 +08:00
|
|
|
file_props['sid'] = jingle.sid
|
2010-06-06 23:22:52 +08:00
|
|
|
jingle.add_content('file', JingleFileTransfer(jingle, file_props))
|
|
|
|
else:
|
|
|
|
jingle = JingleSession(self, weinitiate=True, jid=jid)
|
|
|
|
self.__sessions[jingle.sid] = jingle
|
2010-07-08 16:05:19 +08:00
|
|
|
file_props['sid'] = jingle.sid
|
2010-06-08 21:24:41 +08:00
|
|
|
jingle.add_content('file', JingleFileTransfer(jingle, file_props=file_props))
|
2010-06-06 23:22:52 +08:00
|
|
|
jingle.start_session()
|
|
|
|
return jingle.sid
|
|
|
|
|
2010-03-15 21:34:28 +01:00
|
|
|
|
|
|
|
def iter_jingle_sessions(self, jid, sid=None, media=None):
|
|
|
|
if sid:
|
|
|
|
return (session for session in self.__sessions.values() if session.sid == sid)
|
|
|
|
sessions = (session for session in self.__sessions.values() if session.peerjid == jid)
|
|
|
|
if media:
|
|
|
|
if media not in ('audio', 'video'):
|
|
|
|
return tuple()
|
|
|
|
else:
|
|
|
|
return (session for session in sessions if session.get_content(media))
|
|
|
|
else:
|
|
|
|
return sessions
|
|
|
|
|
|
|
|
|
2010-02-08 15:08:40 +01:00
|
|
|
def get_jingle_session(self, jid, sid=None, media=None):
|
|
|
|
if sid:
|
2010-03-14 21:51:04 +01:00
|
|
|
if sid in self.__sessions:
|
|
|
|
return self.__sessions[sid]
|
2010-02-08 15:08:40 +01:00
|
|
|
else:
|
|
|
|
return None
|
|
|
|
elif media:
|
|
|
|
if media not in ('audio', 'video'):
|
|
|
|
return None
|
|
|
|
for session in self.__sessions.values():
|
|
|
|
if session.peerjid == jid and session.get_content(media):
|
|
|
|
return session
|
|
|
|
|
|
|
|
return None
|