changeset 77bf2919b7ed in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=77bf2919b7ed description: merge jingleFT branch! Thanks to Zhenchao Li and Jefry Lagrange for their work.
diffstat:
src/chat_control.py | 6 +- src/common/caps_cache.py | 4 +- src/common/check_paths.py | 14 + src/common/configpaths.py | 3 +- src/common/connection_handlers.py | 47 +- src/common/connection_handlers_events.py | 101 +- src/common/gajim.py | 6 +- src/common/helpers.py | 4 + src/common/jingle.py | 80 +- src/common/jingle_content.py | 21 +- src/common/jingle_ft.py | 375 ++++++++ src/common/jingle_ftstates.py | 244 +++++ src/common/jingle_rtp.py | 2 +- src/common/jingle_session.py | 211 +++- src/common/jingle_transport.py | 262 ++++++- src/common/jingle_xtls.py | 239 +++++ src/common/protocol/bytestream.py | 66 +- src/common/socks5.py | 1258 +++++++++++++++++++---------- src/common/xmpp/protocol.py | 91 ++- src/dialogs.py | 7 +- src/filetransfers_window.py | 80 +- src/gui_interface.py | 104 ++- src/gui_menu_builder.py | 4 +- src/roster_window.py | 3 + test/unit/test_jingle.py | 157 +++ test/unit/test_socks5.py | 177 ++++ 26 files changed, 2971 insertions(+), 595 deletions(-)
diffs (truncated from 4863 to 300 lines):
diff -r f6f78f3802c0 -r 77bf2919b7ed src/chat_control.py --- a/src/chat_control.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/chat_control.py Sat Apr 14 13:40:55 2012 +0200 @@ -53,7 +53,7 @@ from common.pep import MOODS, ACTIVITIES from common.xmpp.protocol import NS_XHTML, NS_XHTML_IM, NS_FILE, NS_MUC from common.xmpp.protocol import NS_RECEIPTS, NS_ESESSION -from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_ICE_UDP +from common.xmpp.protocol import NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_ICE_UDP, NS_JINGLE_FILE_TRANSFER from common.xmpp.protocol import NS_CHATSTATES from common.connection_handlers_events import MessageOutgoingEvent from common.exceptions import GajimGeneralException @@ -1722,13 +1722,13 @@ self._video_button.set_sensitive(self.video_available)
# Send file - if self.contact.supports(NS_FILE) and (self.type_id == 'chat' or \ + if (self.contact.supports(NS_FILE) or self.contact.supports(NS_JINGLE_FILE_TRANSFER)) and (self.type_id == 'chat' or \ self.gc_contact.resource): self._send_file_button.set_sensitive(True) self._send_file_button.set_tooltip_text('') else: self._send_file_button.set_sensitive(False) - if not self.contact.supports(NS_FILE): + if not (self.contact.supports(NS_FILE) or self.contact.supports(NS_JINGLE_FILE_TRANSFER)): self._send_file_button.set_tooltip_text(_( "This contact does not support file transfer.")) else: diff -r f6f78f3802c0 -r 77bf2919b7ed src/common/caps_cache.py --- a/src/common/caps_cache.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/common/caps_cache.py Sat Apr 14 13:40:55 2012 +0200 @@ -38,10 +38,10 @@ log = logging.getLogger('gajim.c.caps_cache')
from common.xmpp import (NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION, NS_CHATSTATES, - NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_CAPS) + NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_CAPS, NS_JINGLE_FILE_TRANSFER) # Features where we cannot safely assume that the other side supports them FEATURE_BLACKLIST = [NS_CHATSTATES, NS_XHTML_IM, NS_RECEIPTS, NS_ESESSION, - NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO] + NS_JINGLE_ICE_UDP, NS_JINGLE_RTP_AUDIO, NS_JINGLE_RTP_VIDEO, NS_JINGLE_FILE_TRANSFER]
# Query entry status codes NEW = 0 diff -r f6f78f3802c0 -r 77bf2919b7ed src/common/check_paths.py --- a/src/common/check_paths.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/common/check_paths.py Sat Apr 14 13:40:55 2012 +0200 @@ -30,6 +30,7 @@
from common import gajim import logger +from common import jingle_xtls
# DO NOT MOVE ABOVE OF import gajim import sqlite3 as sqlite @@ -266,6 +267,8 @@ MY_DATA = configpaths.gajimpaths['MY_DATA'] MY_CONFIG = configpaths.gajimpaths['MY_CONFIG'] MY_CACHE = configpaths.gajimpaths['MY_CACHE'] + XTLS_CERTS = configpaths.gajimpaths['MY_PEER_CERTS'] + LOCAL_XTLS_CERTS = configpaths.gajimpaths['MY_CERT']
PLUGINS_CONFIG_PATH = gajim.PLUGINS_CONFIG_DIR
@@ -342,6 +345,17 @@ print _('%s is a directory but should be a file') % CACHE_DB_PATH print _('Gajim will now exit') sys.exit() + + if not os.path.exists(XTLS_CERTS): + create_path(XTLS_CERTS) + if not os.path.exists(LOCAL_XTLS_CERTS): + create_path(LOCAL_XTLS_CERTS) + cert_name = os.path.join(LOCAL_XTLS_CERTS, + jingle_xtls.SELF_SIGNED_CERTIFICATE) + if not (os.path.exists(cert_name + '.cert') and os.path.exists( + cert_name + '.pkey')): + jingle_xtls.make_certs(cert_name, 'gajim') +
def create_path(directory): head, tail = os.path.split(directory) diff -r f6f78f3802c0 -r 77bf2919b7ed src/common/configpaths.py --- a/src/common/configpaths.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/common/configpaths.py Sat Apr 14 13:40:55 2012 +0200 @@ -141,7 +141,7 @@ d = {'MY_DATA': '', 'LOG_DB': u'logs.db', 'MY_CACERTS': u'cacerts.pem', 'MY_EMOTS': u'emoticons', 'MY_ICONSETS': u'iconsets', 'MY_MOOD_ICONSETS': u'moods', 'MY_ACTIVITY_ICONSETS': u'activities', - 'PLUGINS_USER': u'plugins'} + 'PLUGINS_USER': u'plugins', 'MY_PEER_CERTS': u'certs'} for name in d: self.add(name, TYPE_DATA, windowsify(d[name]))
@@ -151,6 +151,7 @@ self.add(name, TYPE_CACHE, windowsify(d[name]))
self.add('MY_CONFIG', TYPE_CONFIG, '') + self.add('MY_CERT', TYPE_CONFIG, '')
basedir = fse(os.environ.get(u'GAJIM_BASEDIR', defs.basedir)) self.add('DATA', None, os.path.join(basedir, windowsify(u'data'))) diff -r f6f78f3802c0 -r 77bf2919b7ed src/common/connection_handlers.py --- a/src/common/connection_handlers.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/common/connection_handlers.py Sat Apr 14 13:40:55 2012 +0200 @@ -45,6 +45,7 @@ from common import gajim from common import exceptions from common import dataforms +from common import jingle_xtls from common.commands import ConnectionCommands from common.pubsub import ConnectionPubSub from common.pep import ConnectionPEP @@ -189,7 +190,10 @@ query.setAttr('node', 'http://gajim.org#' + gajim.version.split('-', 1)[ 0]) for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, - common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): + common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS, + common.xmpp.NS_JINGLE_FILE_TRANSFER, common.xmpp.NS_JINGLE_XTLS, + common.xmpp.NS_PUBKEY_PUBKEY, common.xmpp.NS_PUBKEY_REVOKE, + common.xmpp.NS_PUBKEY_ATTEST): feature = common.xmpp.Node('feature') feature.setAttr('var', f) query.addChild(node=feature) @@ -1979,10 +1983,37 @@ gajim.nec.push_incoming_event(SearchFormReceivedEvent(None, conn=self, stanza=iq_obj))
- def _StreamCB(self, con, iq_obj): - log.debug('StreamCB') - gajim.nec.push_incoming_event(StreamReceivedEvent(None, - conn=self, stanza=iq_obj)) + def _search_fields_received(self, con, iq_obj): + jid = jid = helpers.get_jid_from_iq(iq_obj) + tag = iq_obj.getTag('query', namespace = common.xmpp.NS_SEARCH) + if not tag: + self.dispatch('SEARCH_FORM', (jid, None, False)) + return + df = tag.getTag('x', namespace = common.xmpp.NS_DATA) + if df: + self.dispatch('SEARCH_FORM', (jid, df, True)) + return + df = {} + for i in iq_obj.getQueryPayload(): + df[i.getName()] = i.getData() + self.dispatch('SEARCH_FORM', (jid, df, False)) + + def _PubkeyGetCB(self, con, iq_obj): + log.info('PubkeyGetCB') + jid_from = helpers.get_full_jid_from_iq(iq_obj) + sid = iq_obj.getAttr('id') + jingle_xtls.send_cert(con, jid_from, sid) + raise common.xmpp.NodeProcessed + + def _PubkeyResultCB(self, con, iq_obj): + log.info('PubkeyResultCB') + jid_from = helpers.get_full_jid_from_iq(iq_obj) + jingle_xtls.handle_new_cert(con, iq_obj, jid_from) + + def _StreamCB(self, con, obj): + if obj.getTag('conflict'): + # disconnected because of a resource conflict + self.dispatch('RESOURCE_CONFLICT', ())
def _register_handlers(self, con, con_type): # try to find another way to register handlers in each class @@ -2070,5 +2101,7 @@ con.RegisterHandler('iq', self._ResultCB, 'result') con.RegisterHandler('presence', self._StanzaArrivedCB) con.RegisterHandler('message', self._StanzaArrivedCB) - con.RegisterHandler('unknown', self._StreamCB, - common.xmpp.NS_XMPP_STREAMS, xmlns=common.xmpp.NS_STREAMS) + con.RegisterHandler('unknown', self._StreamCB, 'urn:ietf:params:xml:ns:xmpp-streams', xmlns='http://etherx.jabber.org/streams') + con.RegisterHandler('iq', self._PubkeyGetCB, 'get', common.xmpp.NS_PUBKEY_PUBKEY) + con.RegisterHandler('iq', self._PubkeyResultCB, 'result', common.xmpp.NS_PUBKEY_PUBKEY) + diff -r f6f78f3802c0 -r 77bf2919b7ed src/common/connection_handlers_events.py --- a/src/common/connection_handlers_events.py Thu Apr 12 07:17:50 2012 +0200 +++ b/src/common/connection_handlers_events.py Sat Apr 14 13:40:55 2012 +0200 @@ -36,6 +36,7 @@ from common.logger import LOG_DB_PATH from common.pep import SUPPORTED_PERSONAL_USER_EVENTS from common.xmpp.protocol import NS_CHATSTATES +from common.jingle_transport import JingleTransportSocks5
import gtkgui_helpers
@@ -1392,6 +1393,16 @@ self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid) self.sid = self.jingle_session.sid return True + +class JingleTransferCancelledEvent(nec.NetworkIncomingEvent): + name = 'jingleFT-cancelled-received' + base_network_events = [] + + def generate(self): + self.fjid = self.jingle_session.peerjid + self.jid, self.resource = gajim.get_room_and_nick_from_fjid(self.fjid) + self.sid = self.jingle_session.sid + return True
class JingleErrorReceivedEvent(nec.NetworkIncomingEvent): name = 'jingle-error-received' @@ -1901,6 +1912,10 @@ name = 'file-request-received' base_network_events = []
+ def init(self): + self.jingle_content = None + self.FT_content = None + def generate(self): self.get_id() self.fjid = self.conn._ft_get_from(self.stanza) @@ -1908,28 +1923,36 @@ self.file_props = {'type': 'r'} self.file_props['sender'] = self.fjid self.file_props['request-id'] = self.id_ - si = self.stanza.getTag('si') - profile = si.getAttr('profile') - if profile != xmpp.NS_FILE: - self.conn.send_file_rejection(self.file_props, code='400', typ='profile') - raise xmpp.NodeProcessed - feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE) - if not feature_tag: - return - form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA) - if not form_tag: - return - self.dataform = dataforms.ExtendForm(node=form_tag) - for f in self.dataform.iter_fields(): - if f.var == 'stream-method' and f.type == 'list-single': - values = [o[1] for o in f.options] - self.file_props['stream-methods'] = ' '.join(values) - if xmpp.NS_BYTESTREAM in values or xmpp.NS_IBB in values: - break + if self.jingle_content: + self.file_props['session-type'] = 'jingle' + self.file_props['stream-methods'] = xmpp.NS_BYTESTREAM + file_tag = self.jingle_content.getTag('description').getTag( + 'offer').getTag('file') else: - self.conn.send_file_rejection(self.file_props, code='400', typ='stream') - raise xmpp.NodeProcessed - file_tag = si.getTag('file') + si = self.stanza.getTag('si') + profile = si.getAttr('profile') + if profile != xmpp.NS_FILE: + self.conn.send_file_rejection(self.file_props, code='400', + typ='profile') + raise xmpp.NodeProcessed + feature_tag = si.getTag('feature', namespace=xmpp.NS_FEATURE) + if not feature_tag: + return + form_tag = feature_tag.getTag('x', namespace=xmpp.NS_DATA) + if not form_tag: + return + self.dataform = dataforms.ExtendForm(node=form_tag) + for f in self.dataform.iter_fields(): + if f.var == 'stream-method' and f.type == 'list-single': + values = [o[1] for o in f.options] + self.file_props['stream-methods'] = ' '.join(values) + if xmpp.NS_BYTESTREAM in values or xmpp.NS_IBB in values: + break + else: + self.conn.send_file_rejection(self.file_props, code='400', + typ='stream') + raise xmpp.NodeProcessed + file_tag = si.getTag('file') for attribute in file_tag.getAttrs(): if attribute in ('name', 'size', 'hash', 'date'): val = file_tag.getAttr(attribute) @@ -1940,13 +1963,41 @@ if file_desc_tag is not None: self.file_props['desc'] = file_desc_tag.getData()
- mime_type = si.getAttr('mime-type') - if mime_type is not None: - self.file_props['mime-type'] = mime_type + if not self.jingle_content: + mime_type = si.getAttr('mime-type') + if mime_type is not None: + self.file_props['mime-type'] = mime_type
self.file_props['receiver'] = self.conn._ft_get_our_jid() - self.file_props['sid'] = unicode(si.getAttr('id')) self.file_props['transfered_size'] = [] + if self.jingle_content: + self.FT_content.use_security = bool(self.jingle_content.getTag( + 'security')) + self.file_props['session-sid'] = unicode(self.stanza.getTag( + 'jingle').getAttr('sid')) + + self.FT_content.file_props = self.file_props + if not self.FT_content.transport: + self.FT_content.transport = JingleTransportSocks5() + self.FT_content.transport.set_our_jid( + self.FT_content.session.ourjid)