changeset fe9d79c951fa in /home/hg/repos/gajim
details:http://hg.gajim.org/gajim?cmd=changeset;node=fe9d79c951fa description: support for xep-300
diffstat:
src/common/gajim.py | 4 +- src/common/jingle.py | 14 ++++++ src/common/jingle_ft.py | 39 +++++++++++++++-- src/common/jingle_session.py | 27 +++++++++++- src/common/xmpp/protocol.py | 91 ++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 161 insertions(+), 14 deletions(-)
diffs (truncated from 317 to 300 lines):
diff -r 3ccb8751f4f7 -r fe9d79c951fa src/common/gajim.py --- a/src/common/gajim.py Sat Jan 07 20:49:07 2012 -0500 +++ b/src/common/gajim.py Sun Jan 15 19:37:00 2012 -0500 @@ -208,7 +208,9 @@ 'jabber:iq:gateway', xmpp.NS_LAST, xmpp.NS_PRIVACY, xmpp.NS_PRIVATE, xmpp.NS_REGISTER, xmpp.NS_VERSION, xmpp.NS_DATA, xmpp.NS_ENCRYPTED, 'msglog', 'sslc2s', 'stringprep', xmpp.NS_PING, xmpp.NS_TIME_REVISED, xmpp.NS_SSN, - xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX, xmpp.NS_SECLABEL] + xmpp.NS_MOOD, xmpp.NS_ACTIVITY, xmpp.NS_NICK, xmpp.NS_ROSTERX, xmpp.NS_SECLABEL, + xmpp.NS_HASHES, xmpp.NS_HASHES_MD5, xmpp.NS_HASHES_SHA1, + xmpp.NS_HASHES_SHA256, xmpp.NS_HASHES_SHA512]
# Optional features gajim supports per account gajim_optional_features = {} diff -r 3ccb8751f4f7 -r fe9d79c951fa src/common/jingle.py --- a/src/common/jingle.py Sat Jan 07 20:49:07 2012 -0500 +++ b/src/common/jingle.py Sun Jan 15 19:37:00 2012 -0500 @@ -151,10 +151,24 @@ file_props['sid'] = jingle.sid c = JingleFileTransfer(jingle, file_props=file_props, use_security=use_security) + c.hash_algo = self.__hash_support(contact) jingle.add_content('file' + helpers.get_random_string_16(), c) jingle.start_session() return c.transport.sid
+ def __hash_support(self, contact): + + if contact.supports(xmpp.NS_HASHES): + if contact.supports(xmpp.NS_HASHES_MD5): + return 'md5' + elif contact.supports(xmpp.NS_HASHES_SHA1): + return 'sha-1' + elif contact.supports(xmpp.NS_HASHES_SHA256): + return 'sha-256' + elif contact.supports(xmpp.NS_HASHES_SHA512): + return 'sha-512' + + return None
def iter_jingle_sessions(self, jid, sid=None, media=None): if sid: diff -r 3ccb8751f4f7 -r fe9d79c951fa src/common/jingle_ft.py --- a/src/common/jingle_ft.py Sat Jan 07 20:49:07 2012 -0500 +++ b/src/common/jingle_ft.py Sun Jan 15 19:37:00 2012 -0500 @@ -26,7 +26,7 @@ from common import helpers from common.socks5 import Socks5ReceiverClient, Socks5SenderClient from common.connection_handlers_events import FileRequestReceivedEvent - +import threading import logging log = logging.getLogger('gajim.c.jingle_ft')
@@ -56,9 +56,11 @@
# events we might be interested in self.callbacks['session-initiate'] += [self.__on_session_initiate] + self.callbacks['session-initiate-sent'] += [self.__on_session_initiate_sent] self.callbacks['content-add'] += [self.__on_session_initiate] self.callbacks['session-accept'] += [self.__on_session_accept] - self.callbacks['session-terminate'] += [self.__on_session_terminate] + self.callbacks['session-terminate'] += [self.__on_session_terminate] + self.callbacks['session-info'] += [self.__on_session_info] self.callbacks['transport-accept'] += [self.__on_transport_accept] self.callbacks['transport-replace'] += [self.__on_transport_replace] self.callbacks['session-accept-sent'] += [self.__transport_setup] @@ -99,12 +101,36 @@ self.session = session self.media = 'file' self.nominated_cand = {} + + # Hash algorithm that we are using to calculate the integrity of the + # file. Could be 'md5', 'sha-1', etc... + self.hash_algo = None
def __on_session_initiate(self, stanza, content, error, action): gajim.nec.push_incoming_event(FileRequestReceivedEvent(None, conn=self.session.connection, stanza=stanza, jingle_content=content, FT_content=self)) - + def __on_session_initiate_sent(self, stanza, content, error, action): + # Calculate file_hash in a new thread + self.hashThread = threading.Thread(target=self.__calcHash) + self.hashThread.start() + + def __calcHash(self): + if self.hash_algo == None: + return + try: + file = open(self.file_props['file-name'], 'r') + except: + return + h = xmpp.Hashes() + h.calculateHash(self.hash_algo, file) + checksum = xmpp.Node(tag='checksum', + payload=[xmpp.Node(tag='file', payload=[h])]) + checksum.setNamespace(xmpp.NS_JINGLE_FILE_TRANSFER) + # Send hash in a session info + self.session.__session_info(checksum ) + + def __on_session_accept(self, stanza, content, error, action): log.info("__on_session_accept") con = self.session.connection @@ -152,6 +178,9 @@ def __on_session_terminate(self, stanza, content, error, action): log.info("__on_session_terminate")
+ def __on_session_info(self, stanza, content, error, action): + pass + def __on_transport_accept(self, stanza, content, error, action): log.info("__on_transport_accept")
@@ -164,8 +193,6 @@ def __on_transport_info(self, stanza, content, error, action): log.info("__on_transport_info")
- #if not self.weinitiate: # proxy activated from initiator - # return if content.getTag('transport').getTag('candidate-error'): self.nominated_cand['peer-cand'] = False if self.state == STATE_CAND_SENT_PENDING_REPLY: @@ -392,7 +419,7 @@
def start_transfer(self): - + self.state = STATE_TRANSFERING
# It tells wether we start the transfer as client or server diff -r 3ccb8751f4f7 -r fe9d79c951fa src/common/jingle_session.py --- a/src/common/jingle_session.py Sat Jan 07 20:49:07 2012 -0500 +++ b/src/common/jingle_session.py Sun Jan 15 19:37:00 2012 -0500 @@ -98,7 +98,7 @@
self.accepted = True # is this session accepted by user - + self.file_hash = None # callbacks to call on proper contents # use .prepend() to add new callbacks, especially when you're going # to send error instead of ack @@ -126,6 +126,7 @@ 'iq-result': [self.__broadcast], 'iq-error': [self.__on_error], } +
def collect_iq_id(self, iq_id): if iq_id is not None: @@ -416,9 +417,20 @@ def __on_session_info(self, stanza, jingle, error, action): # TODO: ringing, active, (un)hold, (un)mute payload = jingle.getPayload() - if payload: - self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info', type_='modify') - raise xmpp.NodeProcessed + for p in payload: + if p.getName() == 'checksum': + hashes = p.getTag('file').getTag(name='hashes', + namespace=xmpp.NS_HASHES) + for hash in hashes.getChildren(): + algo = hash.getAttr('algo') + if algo in xmpp.Hashes.supported: + data = hash.getData() + self.file_hash = data + print data + raise xmpp.NodeProcessed + self.__send_error(stanza, 'feature-not-implemented', 'unsupported-info', type_='modify') + raise xmpp.NodeProcessed +
def __on_content_remove(self, stanza, jingle, error, action): for content in jingle.iterTags('content'): @@ -704,6 +716,13 @@ if payload: jingle.addChild(node=payload) self.connection.connection.send(stanza) + + def _JingleFileTransfer__session_info(self, p): + # For some strange reason when I call + # self.session.__session_info(h) from the jingleFileTransfer object + # within a thread, this method gets called instead. Even though, it + # isn't being called explicitly. + self.__session_info(p)
def _session_terminate(self, reason=None): assert self.state != JingleStates.ended diff -r 3ccb8751f4f7 -r fe9d79c951fa src/common/xmpp/protocol.py --- a/src/common/xmpp/protocol.py Sat Jan 07 20:49:07 2012 -0500 +++ b/src/common/xmpp/protocol.py Sun Jan 15 19:37:00 2012 -0500 @@ -23,6 +23,7 @@ from simplexml import Node, NodeBuilder import time import string +import hashlib
def ascii_upper(s): trans_table = string.maketrans(string.ascii_lowercase, @@ -89,7 +90,7 @@ NS_JINGLE_RTP = 'urn:xmpp:jingle:apps:rtp:1' # XEP-0167 NS_JINGLE_RTP_AUDIO = 'urn:xmpp:jingle:apps:rtp:audio' # XEP-0167 NS_JINGLE_RTP_VIDEO = 'urn:xmpp:jingle:apps:rtp:video' # XEP-0167 -NS_JINGLE_FILE_TRANSFER='urn:xmpp:jingle:apps:file-transfer:1' # XEP-0234 +NS_JINGLE_FILE_TRANSFER ='urn:xmpp:jingle:apps:file-transfer:1' # XEP-0234 NS_JINGLE_XTLS='urn:xmpp:jingle:security:xtls:0' # XTLS: EXPERIMENTAL security layer of jingle NS_JINGLE_RAW_UDP = 'urn:xmpp:jingle:transports:raw-udp:1' # XEP-0177 NS_JINGLE_ICE_UDP = 'urn:xmpp:jingle:transports:ice-udp:1' # XEP-0176 @@ -160,7 +161,12 @@ NS_PUBKEY_REVOKE = 'urn:xmpp:revoke:2' NS_PUBKEY_ATTEST = 'urn:xmpp:attest:2' NS_STREAM_MGMT = 'urn:xmpp:sm:2' # XEP-198 - +NS_HASHES = 'urn:xmpp:hashes:0' # XEP-300 +NS_HASHES_MD5 = 'urn:xmpp:hash-function-textual-names:md5' +NS_HASHES_SHA1 = 'urn:xmpp:hash-function-textual-names:sha-1' +NS_HASHES_SHA256 = 'urn:xmpp:hash-function-textual-names:sha-256' +NS_HASHES_SHA512 = 'urn:xmpp:hash-function-textual-names:sha-512' + xmpp_stream_error_conditions = ''' bad-format -- -- -- The entity has sent XML that cannot be processed. bad-namespace-prefix -- -- -- The entity has sent a namespace prefix that is unsupported, or has sent no namespace prefix on an element that requires such a prefix. @@ -1030,7 +1036,85 @@ attrs={'id': self.getID()}) iq.setQuery(self.getQuery().getName()).setNamespace(self.getQueryNS()) return iq - + +class Hashes(Node): + """ + Hash elements for various XEPs as defined in XEP-300 + """ + + """ + RECOMENDED HASH USE: + Algorithm Support + MD2 MUST NOT + MD4 MUST NOT + MD5 MAY + SHA-1 MUST + SHA-256 MUST + SHA-512 SHOULD + """ + + supported = ('md5', 'sha-1', 'sha-256', 'sha-512') + + def __init__(self, nsp=NS_HASHES): + Node.__init__(self, None, {}, [], None, None,False, None) + self.setNamespace(nsp) + self.setName('hashes') + + def calculateHash(self, algo, file_string): + """ + Calculate the hash and add it. It is preferable doing it here + instead of doing it all over the place in Gajim. + """ + hl = None + hash = None + + # file_string can be a string or a file + if type(file_string) == str: # if it is a string + if algo == 'md5': + hl = hashlib.md5() + elif algo == 'sha-1': + hl = hashlib.sha1() + elif algo == 'sha-256': + hl = hashlib.sha256() + elif algo == 'sha-512': + hl = hashlib.sha512() + + if hl == None: + # Raise exception + raise Exception('Hash algorithm not supported') + else: + hl.update(file_string) + hash = hl.hexdigest() + else: # if it is a file + + if algo == 'md5': + hl = hashlib.md5() + elif algo == 'sha-1': + hl = hashlib.sha1() + elif algo == 'sha-256': + hl = hashlib.sha256() + elif algo == 'sha-512': + hl = hashlib.sha512() + + if hl == None: + # Raise exception + raise Exception('Hash algorithm not supported') + else: + for line in file_string: + hl.update(line) + hash = hl.hexdigest() + + self.addHash(hash, algo) +