changeset 0e358a669575 in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=0e358a669575
description: Fixed bug when national character (non-ASCII) was entered which search string in ACE (comparison with descriptions, which are unicode, was not possible). It seems that this was Win32 specific (tested on Vista64).
diffstat:
src/advanced.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diffs (12 lines):
diff -r 5cbc539bbe61 -r 0e358a669575 src/advanced.py
--- a/src/advanced.py Sun Jun 28 16:01:43 2009 +0200
+++ b/src/advanced.py Mon Jun 29 06:31:36 2009 +0200
@@ -267,7 +267,7 @@
self.model.append(parent, [name, value, type_])
def visible_func(self, model, treeiter):
- search_string = self.entry.get_text().lower()
+ search_string = self.entry.get_text().decode('utf-8').lower()
for it in tree_model_pre_order(model,treeiter):
if model[it][C_TYPE] != '':
opt_path = self.get_option_path(model, it)
changeset 236a04e1fa69 in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=236a04e1fa69
description: implement jingle negociation for whiteboard
diffstat:
src/chat_control.py | 8 +-
src/common/connection_handlers.py | 10 +-
src/common/jingle.py | 699 +++++++++++++++++++++++++++++++++++++++++++
src/common/xmpp/protocol.py | 6 +
src/common/xmpp/simplexml.py | 3 +
src/dialogs.py | 31 +
src/gajim.py | 114 +++++++-
src/whiteboardwidget.py | 44 +--
8 files changed, 868 insertions(+), 47 deletions(-)
diffs (truncated from 1045 to 300 lines):
diff -r 77f805b84006 -r 236a04e1fa69 src/chat_control.py
--- a/src/chat_control.py Tue Jul 21 15:58:30 2009 +0200
+++ b/src/chat_control.py Tue Jul 21 16:23:34 2009 +0200
@@ -2749,14 +2749,18 @@
self.print_conversation(')', 'status', simple=True)
def _on_whiteboard_button_clicked(self, widget):
+ gajim.connections[self.account].startWhiteboard(
+ self.contact.get_full_jid())
+
+ def draw_whiteboard(self, session):
hbox = self.xml.get_widget('chat_child_hbox')
if len(hbox.get_children()) == 1:
- whiteboard = Whiteboard(self.account, self.contact)
+ whiteboard = Whiteboard(self.account, self.contact, session)
try:
+ # set minimum size
whiteboard.hbox.set_size_request(300, 0)
hbox.pack_start(whiteboard.hbox, expand=False, fill=False)
whiteboard.hbox.show_all()
-
except:
pass # TODO: Fix problem with groupchat?
diff -r 77f805b84006 -r 236a04e1fa69 src/common/connection_handlers.py
--- a/src/common/connection_handlers.py Tue Jul 21 15:58:30 2009 +0200
+++ b/src/common/connection_handlers.py Tue Jul 21 16:23:34 2009 +0200
@@ -51,6 +51,7 @@
from common.commands import ConnectionCommands
from common.pubsub import ConnectionPubSub
from common.caps import ConnectionCaps
+from common.jingle import ConnectionJingle
from common import dbus_support
if dbus_support.supported:
@@ -1413,13 +1414,16 @@
return sess
-class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, ConnectionCommands, ConnectionPubSub, ConnectionCaps, ConnectionHandlersBase):
+class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco,
+ConnectionCommands, ConnectionPubSub, ConnectionCaps, ConnectionHandlersBase,
+ConnectionJingle):
def __init__(self):
ConnectionVcard.__init__(self)
ConnectionBytestream.__init__(self)
ConnectionCommands.__init__(self)
ConnectionPubSub.__init__(self)
ConnectionHandlersBase.__init__(self)
+ ConnectionJingle.__init__(self)
self.gmail_url = None
# keep the latest subscribed event for each jid to prevent loop when we
@@ -2639,6 +2643,10 @@
con.RegisterHandler('iq', self._PrivacySetCB, 'set',
common.xmpp.NS_PRIVACY)
con.RegisterHandler('iq', self._PubSubCB, 'result')
+ con.RegisterHandler('iq', self._JingleCB, 'result')
+ con.RegisterHandler('iq', self._JingleCB, 'error')
+ con.RegisterHandler('iq', self._JingleCB, 'set',
+ common.xmpp.NS_JINGLE)
con.RegisterHandler('iq', self._ErrorCB, 'error')
con.RegisterHandler('iq', self._IqCB)
con.RegisterHandler('iq', self._StanzaArrivedCB)
diff -r 77f805b84006 -r 236a04e1fa69 src/common/jingle.py
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/common/jingle.py Tue Jul 21 16:23:34 2009 +0200
@@ -0,0 +1,699 @@
+##
+## 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.
+##
+''' Handles the jingle signalling protocol. '''
+
+# note: if there will be more types of sessions (possibly file transfer,
+# video...), split this file
+
+import gajim
+import gobject
+import xmpp
+import urllib
+
+import logging
+log = logging.getLogger('gajim.c.jingle')
+
+def timeout_add_and_call(timeout, callable, *args, **kwargs):
+ ''' Call a callback once. If it returns True, add a timeout handler to call it more times.
+ Helper function. '''
+ if callable(*args, **kwargs):
+ return gobject.timeout_add(timeout, callable, *args, **kwargs)
+ return -1 # gobject.source_remove will not object
+
+class JingleStates(object):
+ ''' States in which jingle session may exist. '''
+ ended=0
+ pending=1
+ active=2
+
+class Error(Exception): pass
+class WrongState(Error): pass
+class NoSuchSession(Error): pass
+
+class JingleSession(object):
+ ''' This represents one jingle session. '''
+ def __init__(self, con, weinitiate, jid, sid=None):
+ ''' con -- connection object,
+ weinitiate -- boolean, are we the initiator?
+ jid - jid of the other entity'''
+ self.contents={} # negotiated contents
+ self.connection=con # connection to use
+ # our full jid
+ self.ourjid=gajim.get_jid_from_account(self.connection.name)+'/'+con.server_resource
+ self.peerjid=jid # jid we connect to
+ # jid we use as the initiator
+ self.initiator=weinitiate and self.ourjid or self.peerjid
+ # jid we use as the responder
+ self.responder=weinitiate and self.peerjid or self.ourjid
+ # are we an initiator?
+ self.weinitiate=weinitiate
+ # what state is session in? (one from JingleStates)
+ self.state=JingleStates.ended
+ if not sid:
+ sid=con.connection.getAnID()
+ self.sid=sid # sessionid
+
+ self.accepted=True # is this session accepted by user
+
+ # callbacks to call on proper contents
+ # use .prepend() to add new callbacks, especially when you're going
+ # to send error instead of ack
+ self.callbacks={
+ 'content-accept': [self.__contentAcceptCB, self.__broadcastCB, self.__defaultCB],
+ 'content-add': [self.__defaultCB],
+ 'content-modify': [self.__defaultCB],
+ 'content-remove': [self.__defaultCB],
+ 'session-accept': [self.__contentAcceptCB, self.__broadcastCB, self.__defaultCB],
+ 'session-info': [self.__defaultCB],
+ 'session-initiate': [self.__sessionInitiateCB, self.__broadcastCB, self.__defaultCB],
+ 'session-terminate': [self.__broadcastAllCB, self.__defaultCB],
+ 'transport-info': [self.__broadcastCB, self.__defaultCB],
+ 'iq-result': [],
+ 'iq-error': [],
+ }
+
+ ''' Interaction with user '''
+ def approveSession(self):
+ ''' Called when user accepts session in UI (when we aren't the initiator).'''
+ self.accepted=True
+ self.acceptSession()
+
+ def declineSession(self):
+ ''' Called when user declines session in UI (when we aren't the initiator,
+ or when the user wants to stop session completly. '''
+ self.__sessionTerminate()
+
+ ''' Middle-level functions to manage contents. Handle local content
+ cache and send change notifications. '''
+ def addContent(self, name, content, creator='we'):
+ ''' Add new content to session. If the session is active,
+ this will send proper stanza to update session.
+ The protocol prohibits changing that when pending.
+ Creator must be one of ('we', 'peer', 'initiator', 'responder')'''
+ assert creator in ('we', 'peer', 'initiator', 'responder')
+
+ if self.state==JingleStates.pending:
+ raise WrongState
+
+ if (creator=='we' and self.weinitiate) or (creator=='peer' and not self.weinitiate):
+ creator='initiator'
+ elif (creator=='peer' and self.weinitiate) or (creator=='we' and not self.weinitiate):
+ creator='responder'
+ content.creator = creator
+ content.name = name
+ self.contents[(creator,name)]=content
+
+ if self.state==JingleStates.active:
+ pass # TODO: send proper stanza, shouldn't be needed now
+
+ def removeContent(self, creator, name):
+ ''' We do not need this now '''
+ pass
+
+ def modifyContent(self, creator, name, *someother):
+ ''' We do not need this now '''
+ pass
+
+ def acceptSession(self):
+ ''' Check if all contents and user agreed to start session. '''
+ if not self.weinitiate and self.accepted and \
+ all(c.negotiated for c in self.contents.itervalues()):
+ self.__sessionAccept()
+ ''' Middle-level function to do stanza exchange. '''
+ def startSession(self):
+ ''' Start session. '''
+ self.__sessionInitiate()
+
+ def sendSessionInfo(self): pass
+
+ ''' Session callbacks. '''
+ def stanzaCB(self, stanza):
+ ''' A callback for ConnectionJingle. It gets stanza, then
+ tries to send it to all internally registered callbacks.
+ First one to raise xmpp.NodeProcessed breaks function.'''
+ jingle = stanza.getTag('jingle')
+ error = stanza.getTag('error')
+ if error:
+ # it's an iq-error stanza
+ action = 'iq-error'
+ elif jingle:
+ # it's a jingle action
+ action = jingle.getAttr('action')
+ else:
+ # it's an iq-result (ack) stanza
+ action = 'iq-result'
+
+ callables = self.callbacks[action]
+
+ try:
+ for callable in callables:
+ callable(stanza=stanza, jingle=jingle, error=error, action=action)
+ except xmpp.NodeProcessed:
+ pass
+
+ def __defaultCB(self, stanza, jingle, error, action):
+ ''' Default callback for action stanzas -- simple ack
+ and stop processing. '''
+ response = stanza.buildReply('result')
+ self.connection.connection.send(response)
+
+ def __contentAcceptCB(self, stanza, jingle, error, action):
+ ''' Called when we get content-accept stanza or equivalent one
+ (like session-accept).'''
+ # check which contents are accepted
+ for content in jingle.iterTags('content'):
+ creator = content['creator']
+ name = content['name']
+
+ def __sessionInitiateCB(self, stanza, jingle, error, action):
+ ''' We got a jingle session request from other entity,
+ therefore we are the receiver... Unpack the data,
+ inform the user. '''
+ self.initiator = jingle['initiator']
+ self.responder = self.ourjid
+ self.peerjid = self.initiator
+ self.accepted = False # user did not accept this session yet
+
+ # TODO: If the initiator is unknown to the receiver (e.g., via presence
+ # subscription) and the receiver has a policy of not communicating via
+ # Jingle with unknown entities, it SHOULD return a <service-unavailable/>
+ # error.
+
+ # Lets check what kind of jingle session does the peer want
+ fail = True
+ contents = []
+ 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_RTP and tran_ns==xmpp.NS_JINGLE_ICE_UDP:
+ # we've got voip content
+ self.addContent(element['name'], JingleVoiP(self), 'peer')
+ contents.append(('VOIP',))
+ fail = False
+ if desc_ns==xmpp.NS_JINGLE_XHTML and tran_ns==xmpp.NS_JINGLE_SXE:
+ # we've got whiteboard content
+ self.addContent(element['name'], JingleWhiteboard(self), 'peer')
+ contents.append(('WHITEBOARD',))
+ fail = False
+
+ # If there's no content we understand...
+ if fail:
+ # TODO: we should send <unsupported-content/> inside too
+ # TODO: delete this instance
+ self.connection.connection.send(
+ xmpp.Error(stanza, xmpp.NS_STANZAS + 'feature-not-implemented'))
+ self.connection.deleteJingle(self)
+ raise xmpp.NodeProcessed
+
+ self.state = JingleStates.pending
+
+ # Send event about starting a session
+ self.connection.dispatch('JINGLE_INCOMING', (self.initiator, self.sid, contents))
+
+ def __broadcastCB(self, stanza, jingle, error, action):
+ ''' Broadcast the stanza contents to proper content handlers. '''
+ for content in jingle.iterTags('content'):
+ name = content['name']
+ creator = content['creator']
+ cn = self.contents[(creator, name)]
+ cn.stanzaCB(stanza, content, error, action)
+
+ def __broadcastAllCB(self, stanza, jingle, error, action):