diff --git a/src/chat_control.py b/src/chat_control.py
index 91ca40c13..5be477f76 100644
--- a/src/chat_control.py
+++ b/src/chat_control.py
@@ -1192,8 +1192,7 @@ class ChatControl(ChatControlBase):
widget.get_active())
def _on_start_voip_menuitem_activate(self, *things):
- print 'Start VoiP'
- gajim.connections[self.account].startVoiP(self.contact.jid)
+ gajim.connections[self.account].startVoiP(self.contact.jid+'/'+self.contact.resource)
def _update_gpg(self):
tb = self.xml.get_widget('gpg_togglebutton')
diff --git a/src/common/jingle.py b/src/common/jingle.py
index dac64e4c1..737fdfb45 100644
--- a/src/common/jingle.py
+++ b/src/common/jingle.py
@@ -30,7 +30,7 @@ class NoCommonCodec(Exception): pass
class JingleSession(object):
''' This represents one jingle session. '''
__metaclass__=meta.VerboseClassType
- def __init__(self, con, weinitiate, jid):
+ def __init__(self, con, weinitiate, jid, sid=None):
''' con -- connection object,
weinitiate -- boolean, are we the initiator?
jid - jid of the other entity'''
@@ -47,19 +47,26 @@ class JingleSession(object):
self.weinitiate=weinitiate
# what state is session in? (one from JingleStates)
self.state=JingleStates.ended
- self.sid=con.connection.getAnID() # sessionid
+ if not sid:
+ sid=con.connection.getAnID()
+ self.sid=sid # sessionid
# callbacks to call on proper contents
- # use .prepend() to add new callbacks
- self.callbacks=dict((key, [self.__defaultCB]) for key in
- ('content-add', 'content-modify',
- 'content-remove', 'session-accept', 'session-info',
- 'session-initiate', 'session-terminate',
- 'transport-info'))
- self.callbacks['iq-result']=[]
- self.callbacks['iq-error']=[]
-
- self.callbacks['content-accept']=[self.__contentAcceptCB, self.__defaultCB]
+ # use .prepend() to add new callbacks, especially when you're going
+ # to send error instead of ack
+ self.callbacks={
+ 'content-accept': [self.__contentAcceptCB, self.__defaultCB],
+ 'content-add': [self.__defaultCB],
+ 'content-modify': [self.__defaultCB],
+ 'content-remove': [self.__defaultCB],
+ 'session-accept': [self.__contentAcceptCB, self.__defaultCB],
+ 'session-info': [self.__defaultCB],
+ 'session-initiate': [self.__sessionInitiateCB, self.__defaultCB],
+ 'session-terminate': [self.__defaultCB],
+ 'transport-info': [self.__defaultCB],
+ 'iq-result': [],
+ 'iq-error': [],
+ }
''' Middle-level functions to manage contents. Handle local content
cache and send change notifications. '''
@@ -93,7 +100,7 @@ class JingleSession(object):
''' Middle-level function to do stanza exchange. '''
def startSession(self):
''' Start session. '''
- self.__sessionInitiate(self)
+ self.__sessionInitiate()
def sendSessionInfo(self): pass
def sendTransportInfo(self): pass
@@ -128,8 +135,7 @@ class JingleSession(object):
''' Default callback for action stanzas -- simple ack
and stop processing. '''
response = stanza.buildReply('result')
- self.connection.send(response)
- raise xmpp.NodeProcessed
+ self.connection.connection.send(response)
def __contentAcceptCB(self, stanza, jingle, error):
''' Called when we get content-accept stanza or equivalent one
@@ -140,10 +146,37 @@ class JingleSession(object):
name = content['name']
+ def sessionInitiateCB(self, stanza):
+ ''' We got a jingle session request from other entity,
+ therefore we are the receiver... Unpack the data. '''
+ jingle = stanza.getTag('jingle')
+ self.initiator = jingle['initiator']
+ self.responder = self.ourjid
+ self.jid = self.initiator
+
+ fail = True
+ for element in jingle.iterTags('content'):
+ # checking what kind of session this will be
+ desc_ns = element.getTag('description').getNamespace()
+ tran_ns = element.getTag('transport').getNamespace()
+ if desc_ns==xmpp.NS_JINGLE_AUDIO and tran_ns==xmpp.NS_JINGLE_ICE_UDP:
+ # we've got voip content
+ self.addContent(element['name'], JingleVoiP(self, node=element), 'peer')
+ fail = False
+
+ if fail:
+ # TODO: we should send inside too
+ self.connection.connection.send(
+ xmpp.Error(stanza, xmpp.NS_STANZAS + 'feature-not-implemented'))
+ self.connection.deleteJingle(self)
+ raise xmpp.NodeProcessed
+
+ self.state = JingleStates.pending
+
''' Methods that make/send proper pieces of XML. They check if the session
is in appropriate state. '''
def __makeJingle(self, action):
- stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.jid))
+ stanza = xmpp.Iq(typ='set', to=xmpp.JID(self.peerjid))
jingle = stanza.addChild('jingle', attrs={
'xmlns': 'http://www.xmpp.org/extensions/xep-0166.html#ns',
'action': action,
@@ -161,7 +194,6 @@ class JingleSession(object):
else:
jingle.addChild('content',
attrs={'name': content.name, 'creator': content.creator})
- return c
def __appendContents(self, jingle, full=True):
''' Append all elements to .'''
@@ -174,13 +206,13 @@ class JingleSession(object):
assert self.state==JingleStates.ended
stanza, jingle = self.__makeJingle('session-initiate')
self.__appendContents(jingle)
- self.connection.send(jingle)
+ self.connection.connection.send(stanza)
def __sessionAccept(self):
assert self.state==JingleStates.pending
stanza, jingle = self.__jingle('session-accept')
self.__appendContents(jingle, False)
- self.connection.send(stanza)
+ self.connection.connection.send(stanza)
self.state=JingleStates.active
def __sessionInfo(self, payload=None):
@@ -188,12 +220,12 @@ class JingleSession(object):
stanza, jingle = self.__jingle('session-info')
if payload:
jingle.addChild(node=payload)
- self.connection.send(stanza)
+ self.connection.connection.send(stanza)
def __sessionTerminate(self):
assert self.state!=JingleStates.ended
stanza, jingle = self.__jingle('session-terminate')
- self.connection.send(stanza)
+ self.connection.connection.send(stanza)
def __contentAdd(self):
assert self.state==JingleStates.active
@@ -211,59 +243,49 @@ class JingleSession(object):
assert self.state!=JingleStates.ended
'''Callbacks'''
- def sessionInitiateCB(self, stanza):
- ''' We got a jingle session request from other entity,
- therefore we are the receiver... Unpack the data. '''
- jingle = stanza.getTag('jingle')
- self.initiator = jingle['initiator']
- self.responder = self.ourjid
- self.jid = self.initiator
- self.state = JingleStates.pending
- self.sid = jingle['sid']
- for element in jingle.iterTags('content'):
- content={'creator': 'initiator',
- 'name': element['name'],
- 'description': element.getTag('description'),
- 'transport': element.getTag('transport')}
- if element.has_attr('profile'):
- content['profile']=element['profile']
- self.contents[('initiator', content['name'])]=content
-
def sessionTerminateCB(self, stanza): pass
+class Codec(object):
+ ''' This class keeps description of a single codec. '''
+ def __init__(self, name, id=None, **params):
+ ''' Create new codec description. '''
+ self.name = name
+ self.id = id
+ self.attrs = {'name': self.name, 'id': self.id, 'channels': 1}
+ for key in ('channels', 'clockrate', 'maxptime', 'ptime'):
+ if key in params:
+ self.attrs[key]=params[key]
+ del params[key]
+ self.params = params
+
+ def __eq__(a, b):
+ ''' Compare two codec descriptions. '''
+ # TODO: check out what should be tested...
+ if a.name!=b.name: return False
+ # ...
+ return True
+
+ def toXML(self):
+ return xmpp.Node('payload',
+ attrs=self.attrs,
+ payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params))
+
class JingleAudioSession(object):
__metaclass__=meta.VerboseClassType
- class Codec(object):
- ''' This class keeps description of a single codec. '''
- def __init__(self, name, id=None, **params):
- ''' Create new codec description. '''
- self.name = name
- self.id = id
- self.attrs = {'name': self.name, 'id': self.id, 'channels': 1}
- for key in ('channels', 'clockrate', 'maxptime', 'ptime'):
- if key in params:
- self.attrs[key]=params[key]
- del params[key]
- self.params = params
-
- def __eq__(a, b):
- ''' Compare two codec descriptions. '''
- # TODO: check out what should be tested...
- if a.name!=b.name: return False
- # ...
- return True
-
- def toXML(self):
- return xmpp.Node('payload',
- attrs=self.attrs,
- payload=(xmpp.Node('parameter', {'name': k, 'value': v}) for k,v in self.params))
-
- def __init__(self, content):
+ def __init__(self, content, fromNode):
self.content = content
self.initiator_codecs=[]
self.responder_codecs=[]
+ if fromNode:
+ # read all codecs peer understand
+ for payload in fromNode.iterTags('payload-type'):
+ attrs = fromNode.getAttrs().copy()
+ for param in fromNode.iterTags('parameter'):
+ attrs[param['name']]=param['value']
+ self.initiator_codecs.append(Codec(**attrs))
+
def sessionInitiateCB(self, stanza, ourcontent):
pass
@@ -284,7 +306,7 @@ class JingleAudioSession(object):
our_l = supported_codecs[:]
out = []
ids = range(128)
- for codec in other:
+ for codec in other_l:
if codec in our_l:
out.append(codec)
our_l.remove(codec)
@@ -304,8 +326,7 @@ class JingleAudioSession(object):
''' Methods for making proper pieces of XML. '''
def __codecsList(self, codecs):
''' Prepares a description element with codecs given as a parameter. '''
- return xmpp.Node('description',
- xmlns=xmpp.NS_JINGLE_AUDIO,
+ return xmpp.Node(xmpp.NS_JINGLE_AUDIO+' description',
payload=(codec.toXML() for codec in codecs))
def toXML(self):
@@ -329,25 +350,26 @@ class JingleICEUDPSession(object):
def toXML(self):
''' ICE-UDP doesn't send much in its transport stanza... '''
- return xmpp.Node('transport', xmlns=xmpp.JINGLE_ICE_UDP)
+ return xmpp.Node(xmpp.NS_JINGLE_ICE_UDP+' transport')
class JingleVoiP(object):
''' Jingle VoiP sessions consist of audio content transported
over an ICE UDP protocol. '''
__metaclass__=meta.VerboseClassType
- def __init__(self):
- self.audio = JingleAudioSession(self)
+ def __init__(self, session, node=None):
+ self.session = session
+
+ if node is None:
+ self.audio = JingleAudioSession(self)
+ else:
+ self.audio = JingleAudioSession(self, node.getTag('content'))
self.transport = JingleICEUDPSession(self)
def toXML(self):
''' Return proper XML for element. '''
return xmpp.Node('content',
attrs={'name': self.name, 'creator': self.creator, 'profile': 'RTP/AVP'},
- childs=[self.audio.toXML(), self.transport.toXML()])
-
- def _sessionInitiateCB(self):
- ''' Called when we initiate the session. '''
- self.transport._sessionInitiateCB()
+ payload=[self.audio.toXML(), self.transport.toXML()])
class ConnectionJingle(object):
''' This object depends on that it is a part of Connection class. '''
@@ -391,12 +413,14 @@ class ConnectionJingle(object):
# do we need to create a new jingle object
if (jid, sid) not in self.__sessions:
- # we should check its type here...
- newjingle = JingleAudioSession(con=self, weinitiate=False, jid=jid)
+ # TODO: we should check its type here...
+ newjingle = JingleSession(con=self, weinitiate=False, jid=jid, sid=sid)
self.addJingle(newjingle)
# we already have such session in dispatcher...
- return self.__sessions[(jid, sid)].stanzaCB(stanza)
+ self.__sessions[(jid, sid)].stanzaCB(stanza)
+
+ raise xmpp.NodeProcessed
def addJingleIqCallback(self, jid, id, jingle):
self.__iq_responses[(jid, id)]=jingle