Show
Ignore:
Timestamp:
12/12/07 09:44:46 (12 months ago)
Author:
asterix
Message:

merge diff from trunk

Files:
1 modified

Legend:

Unmodified
Added
Removed
  • branches/pep/src/common/connection_handlers.py

    r8487 r9123  
    33## 
    44## Contributors for this file: 
    5 ##      - Yann Le Boulanger <asterix@lagaule.org> 
     5##      - Yann Leboulanger <asterix@lagaule.org> 
    66##      - Nikos Kouremenos <kourem@gmail.com> 
    77##      - Dimitur Kirov <dkirov@gmail.com> 
    88##      - Travis Shirk <travis@pobox.com> 
    99## 
    10 ## This program is free software; you can redistribute it and/or modify 
     10## This file is part of Gajim. 
     11## 
     12## Gajim is free software; you can redistribute it and/or modify 
    1113## it under the terms of the GNU General Public License as published 
    12 ## by the Free Software Foundation; version 2 only. 
     14## by the Free Software Foundation; version 3 only. 
    1315## 
    14 ## This program is distributed in the hope that it will be useful, 
     16## Gajim is distributed in the hope that it will be useful, 
    1517## but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1618## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1719## GNU General Public License for more details. 
     20## 
     21## You should have received a copy of the GNU General Public License 
     22## along with Gajim.  If not, see <http://www.gnu.org/licenses/>. 
    1823## 
    1924 
     
    2530 
    2631from time import (altzone, daylight, gmtime, localtime, mktime, strftime, 
    27                   time as time_time, timezone, tzname) 
     32        time as time_time, timezone, tzname) 
    2833from calendar import timegm 
    2934 
     
    4550        import dbus 
    4651        from music_track_listener import MusicTrackListener 
     52 
     53from common.stanza_session import EncryptedStanzaSession  
    4754 
    4855STATUS_LIST = ['offline', 'connecting', 'online', 'chat', 'away', 'xa', 'dnd', 
     
    6572        def __init__(self): 
    6673                self.files_props = {} 
    67          
    68         def is_transfer_stoped(self, file_props): 
     74 
     75        def is_transfer_stopped(self, file_props): 
    6976                if file_props.has_key('error') and file_props['error'] != 0: 
    7077                        return True 
     
    95102                ''' stop all active transfer for contact ''' 
    96103                for file_props in self.files_props.values(): 
    97                         if self.is_transfer_stoped(file_props): 
     104                        if self.is_transfer_stopped(file_props): 
    98105                                continue 
    99106                        receiver_jid = unicode(file_props['receiver']).split('/')[0] 
     
    183190                                ft_add_hosts_to_send.split(',')) 
    184191                        for ft_host in ft_add_hosts_to_send: 
    185                                 try: 
    186                                         ft_host = socket.gethostbyname(ft_host) 
    187                                         ft_add_hosts.append(ft_host) 
    188                                 except socket.gaierror: 
    189                                         self.dispatch('ERROR', (_('Wrong host'), _('The host %s you configured as the ft_add_hosts_to_send advanced option is not valid, so ignored.') % ft_host)) 
     192                                ft_add_hosts.append(ft_host) 
    190193                listener = gajim.socks5queue.start_listener(port, 
    191194                        sha_str, self._result_socks5_sid, file_props['sid']) 
     
    213216                        ostreamhost.setAttr('host', ft_host) 
    214217                        ostreamhost.setAttr('jid', sender) 
    215                 for thehost in self.peerhost: 
    216                         try: 
    217                                 thehost = self.peerhost[0] 
    218                                 streamhost = common.xmpp.Node(tag = 'streamhost') # My IP 
    219                                 query.addChild(node = streamhost) 
    220                                 streamhost.setAttr('port', unicode(port)) 
    221                                 streamhost.setAttr('host', thehost) 
    222                                 streamhost.setAttr('jid', sender) 
    223                         except socket.gaierror: 
    224                                 self.dispatch('ERROR', (_('Wrong host'), 
    225                                         _('Invalid local address? :-O'))) 
     218                try: 
     219                        thehost = self.peerhost[0] 
     220                        streamhost = common.xmpp.Node(tag = 'streamhost') # My IP 
     221                        query.addChild(node = streamhost) 
     222                        streamhost.setAttr('port', unicode(port)) 
     223                        streamhost.setAttr('host', thehost) 
     224                        streamhost.setAttr('jid', sender) 
     225                except socket.gaierror: 
     226                        self.dispatch('ERROR', (_('Wrong host'), 
     227                                _('Invalid local address? :-O'))) 
    226228 
    227229                if fast and proxyhosts != [] and gajim.config.get_per('accounts', 
     
    354356                query = iq.setTag('query') 
    355357                query.setNamespace(common.xmpp.NS_BYTESTREAM) 
    356                 query.setAttr('sid',  proxy['sid']) 
     358                query.setAttr('sid', proxy['sid']) 
    357359                activate = query.setTag('activate') 
    358360                activate.setData(file_props['proxy_receiver']) 
     
    445447                 
    446448                try: 
    447                         streamhost =  query.getTag('streamhost-used') 
     449                        streamhost = query.getTag('streamhost-used') 
    448450                except: # this bytestream result is not what we need 
    449451                        pass 
     
    474476 
    475477                if real_id[:3] == 'au_': 
    476                         gajim.socks5queue.send_file(file_props, self.name) 
     478                        if file.has_key('stopped') and file_props['stopped']: 
     479                                self.remove_transfer(file_props) 
     480                        else: 
     481                                gajim.socks5queue.send_file(file_props, self.name) 
    477482                        raise common.xmpp.NodeProcessed 
    478483 
     
    496501 
    497502                else: 
    498                         gajim.socks5queue.send_file(file_props, self.name) 
     503                        if file_props.has_key('stopped') and file_props['stopped']: 
     504                                self.remove_transfer(file_props) 
     505                        else: 
     506                                gajim.socks5queue.send_file(file_props, self.name) 
    499507                        if file_props.has_key('fast'): 
    500508                                fasts = file_props['fast'] 
     
    658666                iq.setAttr('id', id) 
    659667                query = iq.setTag('query') 
    660                 query.setAttr('node','http://gajim.org/caps#' + gajim.version) 
     668                query.setAttr('node','http://gajim.org/caps#' + gajim.version.split('-', 
     669                        1)[0]) 
    661670                for f in (common.xmpp.NS_BYTESTREAM, common.xmpp.NS_SI, \ 
    662671                                                common.xmpp.NS_FILE, common.xmpp.NS_COMMANDS): 
     
    710719        def _DiscoverItemsGetCB(self, con, iq_obj): 
    711720                gajim.log.debug('DiscoverItemsGetCB') 
     721                if self.commandItemsQuery(con, iq_obj): 
     722                        raise common.xmpp.NodeProcessed 
    712723                node = iq_obj.getTagAttr('query', 'node') 
    713                 if node is None: 
     724                if node is None: 
    714725                        result = iq_obj.buildReply('result') 
    715726                        self.connection.send(result) 
     
    724735                node = q.getAttr('node') 
    725736 
    726                 if self.commandQuery(con, iq_obj): 
     737                if self.commandInfoQuery(con, iq_obj): 
    727738                        raise common.xmpp.NodeProcessed 
    728739                 
     
    737748                        if node and node.find('#') != -1: 
    738749                                extension = node[node.index('#') + 1:] 
    739                         client_version = 'http://gajim.org/caps#' + gajim.version 
     750                        client_version = 'http://gajim.org/caps#' + gajim.version.split('-', 
     751                                1)[0] 
    740752 
    741753                        if node in (None, client_version): 
     
    746758                                q.addChild('feature', attrs = {'var': common.xmpp.NS_COMMANDS}) 
    747759                                q.addChild('feature', attrs = {'var': common.xmpp.NS_DISCO_INFO}) 
    748                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY}) 
    749                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY + '+notify'}) 
    750                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE}) 
    751                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE + '+notify'}) 
    752                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD}) 
    753                                 q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD + '+notify'}) 
     760                                if gajim.config.get('use_pep'): 
     761                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY}) 
     762                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_ACTIVITY + '+notify'}) 
     763                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE}) 
     764                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_TUNE + '+notify'}) 
     765                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD}) 
     766                                        q.addChild('feature', attrs = {'var': common.xmpp.NS_MOOD + '+notify'}) 
     767                                q.addChild('feature', attrs = {'var': common.xmpp.NS_ESESSION_INIT}) 
    754768 
    755769                        if (node is None or extension == 'cstates') and gajim.config.get('outgoing_chat_state_notifactions') != 'disabled': 
     
    793807                                        attr[key] = i.getAttr(key) 
    794808                                if attr.has_key('category') and \ 
    795                                    attr['category'] in ('gateway', 'headline') and \ 
    796                                    attr.has_key('type'): 
     809                                        attr['category'] in ('gateway', 'headline') and \ 
     810                                        attr.has_key('type'): 
    797811                                        transport_type = attr['type'] 
    798812                                if attr.has_key('category') and \ 
    799                                    attr['category'] == 'conference' and \ 
    800                                    attr.has_key('type') and attr['type'] == 'text': 
     813                                        attr['category'] == 'conference' and \ 
     814                                        attr.has_key('type') and attr['type'] == 'text': 
    801815                                        is_muc = True 
    802816                                identities.append(attr) 
     
    869883                if len(ext): 
    870884                        c.setAttr('ext', ' '.join(ext)) 
    871                 c.setAttr('ver', gajim.version) 
     885                c.setAttr('ver', gajim.version.split('-', 1)[0]) 
    872886                return p 
    873887         
     
    10831097                                        data = {'jid': jid} 
    10841098                                        order = meta.getAttr('order') 
     1099                                        try: 
     1100                                                order = int(order) 
     1101                                        except: 
     1102                                                order = 0 
    10851103                                        if order != None: 
    10861104                                                data['order'] = order 
     
    10911109                                self.dispatch('METACONTACTS', meta_list) 
    10921110                        else: 
    1093                                 self.metacontacts_supported = False 
     1111                                if iq_obj.getErrorCode() not in ('403', '406', '404'): 
     1112                                        self.private_storage_supported = False 
    10941113                        # We can now continue connection by requesting the roster 
    10951114                        self.connection.initRoster() 
     
    12081227                ConnectionCommands.__init__(self) 
    12091228                ConnectionPubSub.__init__(self) 
    1210                 self.gmail_url=None 
     1229                self.gmail_url = None 
    12111230                # List of IDs we are waiting answers for {id: (type_of_request, data), } 
    12121231                self.awaiting_answers = {} 
     
    12171236                # SUBSCRIBED event to gui 
    12181237                self.automatically_added = [] 
    1219                 # keep the latest subscribed event for each jid to prevent loop when we  
     1238                # keep the latest subscribed event for each jid to prevent loop when we 
    12201239                # acknoledge presences 
    12211240                self.subscribed_events = {} 
     1241 
     1242                # keep track of sessions this connection has with other JIDs 
     1243                self.sessions = {} 
    12221244                try: 
    12231245                        idle.init() 
    12241246                except: 
    12251247                        HAS_IDLE = False 
    1226          
     1248 
    12271249        def build_http_auth_answer(self, iq_obj, answer): 
    12281250                if answer == 'yes': 
     
    12441266                        msg = iq_obj.getTagData('body') # In case it's a message with a body 
    12451267                        self.dispatch('HTTP_AUTH', (method, url, id, iq_obj, msg)); 
     1268                raise common.xmpp.NodeProcessed 
     1269 
     1270        def _FeatureNegCB(self, con, stanza, session): 
     1271                gajim.log.debug('FeatureNegCB') 
     1272                feature = stanza.getTag(name='feature', namespace=common.xmpp.NS_FEATURE) 
     1273                form = common.xmpp.DataForm(node=feature.getTag('x')) 
     1274 
     1275                if form['FORM_TYPE'] == 'urn:xmpp:ssn': 
     1276                        self.dispatch('SESSION_NEG', (stanza.getFrom(), session, form)) 
     1277                else: 
     1278                        reply = stanza.buildReply() 
     1279                        reply.setType('error') 
     1280 
     1281                        reply.addChild(feature) 
     1282                        reply.addChild(node=xmpp.ErrorNode('service-unavailable', typ='cancel')) 
     1283 
     1284                        con.send(reply) 
     1285                 
     1286                raise common.xmpp.NodeProcessed 
     1287 
     1288        def _InitE2ECB(self, con, stanza, session): 
     1289                gajim.log.debug('InitE2ECB') 
     1290                init = stanza.getTag(name='init', namespace=common.xmpp.NS_ESESSION_INIT) 
     1291                form = common.xmpp.DataForm(node=init.getTag('x')) 
     1292 
     1293                self.dispatch('SESSION_NEG', (stanza.getFrom(), session, form)) 
     1294 
    12461295                raise common.xmpp.NodeProcessed 
    12471296 
     
    13141363                        ns = storage_tag.getNamespace() 
    13151364                        if ns == 'storage:metacontacts': 
    1316                                 self.metacontacts_supported = False 
     1365                                self.private_storage_supported = False 
    13171366                                # Private XML Storage (XEP49) is not supported by server 
    13181367                                # Continue connecting 
     
    13221371                gajim.log.debug('rosterSetCB') 
    13231372                for item in iq_obj.getTag('query').getChildren(): 
    1324                         jid  = helpers.parse_jid(item.getAttr('jid')) 
     1373                        jid = helpers.parse_jid(item.getAttr('jid')) 
    13251374                        name = item.getAttr('name') 
    1326                         sub  = item.getAttr('subscription') 
    1327                         ask  = item.getAttr('ask') 
     1375                        sub = item.getAttr('subscription') 
     1376                        ask = item.getAttr('ask') 
    13281377                        groups = [] 
    13291378                        for group in item.getTags('group'): 
     
    14001449                qp.setTagData('utc', strftime('%Y%m%dT%T', gmtime())) 
    14011450                qp.setTagData('tz', tzname[daylight]) 
    1402                 qp.setTagData('display', strftime('%c', localtime())) 
     1451                qp.setTagData('display', helpers.decode_string(strftime('%c', 
     1452                        localtime()))) 
    14031453                self.connection.send(iq_obj) 
    14041454                raise common.xmpp.NodeProcessed 
     
    14081458                iq_obj = iq_obj.buildReply('result') 
    14091459                qp = iq_obj.setTag('time', 
    1410                                    namespace=common.xmpp.NS_TIME_REVISED) 
     1460                        namespace=common.xmpp.NS_TIME_REVISED) 
    14111461                qp.setTagData('utc', strftime('%Y-%m-%dT%TZ', gmtime())) 
    14121462                zone = -(timezone, altzone)[daylight] / 60 
     
    14611511        def _messageCB(self, con, msg): 
    14621512                '''Called when we receive a message''' 
     1513                frm = helpers.get_full_jid_from_iq(msg) 
     1514                mtype = msg.getType() 
     1515                thread_id = msg.getThread() 
     1516 
     1517                if not mtype: 
     1518                        mtype = 'normal' 
     1519 
     1520                if not mtype == 'groupchat': 
     1521                        session = self.get_session(frm, thread_id, mtype) 
     1522 
     1523                if thread_id and not session.received_thread_id: 
     1524                        session.received_thread_id = True 
     1525 
    14631526                # check if the message is pubsub#event 
    14641527                if msg.getTag('event') is not None: 
     
    14701533                        self._HttpAuthCB(con, msg) 
    14711534                        return 
     1535                if msg.getTag('feature') and msg.getTag('feature').namespace == \ 
     1536                common.xmpp.NS_FEATURE: 
     1537                        if gajim.HAVE_PYCRYPTO: 
     1538                                self._FeatureNegCB(con, msg, session) 
     1539                        return 
     1540                if msg.getTag('init') and msg.getTag('init').namespace == \ 
     1541                common.xmpp.NS_ESESSION_INIT: 
     1542                        self._InitE2ECB(con, msg, session) 
     1543                 
     1544                encrypted = False 
     1545                tim = msg.getTimestamp() 
     1546                tim = helpers.datetime_tuple(tim) 
     1547                tim = localtime(timegm(tim)) 
     1548 
     1549                e2e_tag = msg.getTag('c', namespace = common.xmpp.NS_STANZA_CRYPTO) 
     1550                if e2e_tag: 
     1551                        encrypted = True 
     1552 
     1553                        try: 
     1554                                msg = session.decrypt_stanza(msg) 
     1555                        except: 
     1556                                self.dispatch('FAILED_DECRYPT', (frm, tim)) 
     1557 
    14721558                msgtxt = msg.getBody() 
    14731559                msghtml = msg.getXHTML() 
    1474                 mtype = msg.getType() 
    14751560                subject = msg.getSubject() # if not there, it's None 
    14761561                tim = msg.getTimestamp() 
     
    14911576                        no_log_for = '' 
    14921577                no_log_for = no_log_for.split() 
    1493                 encrypted = False 
    14941578                chatstate = None 
    14951579                encTag = msg.getTag('x', namespace = common.xmpp.NS_ENCRYPTED) 
     
    15111595                        if xtag.getNamespace() == common.xmpp.NS_CONFERENCE and not invite: 
    15121596                                room_jid = xtag.getAttr('jid') 
    1513                                 self.dispatch('GC_INVITATION', (room_jid, frm, '', None)) 
     1597                                is_continued = False 
     1598                                if xtag.getTag('continue'): 
     1599                                        is_continued = True 
     1600                                self.dispatch('GC_INVITATION', (room_jid, frm, '', None, 
     1601                                        is_continued)) 
    15141602                                return 
     1603                form_node = None 
     1604                for xtag in xtags: 
     1605                        if xtag.getNamespace() == common.xmpp.NS_DATA: 
     1606                                form_node = xtag 
     1607                                break 
    15151608                # chatstates - look for chatstate tags in a message if not delayed 
    15161609                if not delayed: 
     
    15381631                        #decrypt 
    15391632                        encmsg = encTag.getData() 
    1540                          
     1633 
    15411634                        keyID = gajim.config.get_per('accounts', self.name, 'keyid') 
    15421635                        if keyID: 
    15431636                                decmsg = self.gpg.decrypt(encmsg, keyID) 
     1637                                # \x00 chars are not allowed in C (so in GTK) 
     1638                                decmsg = decmsg.replace('\x00', '') 
    15441639                if decmsg: 
    15451640                        msgtxt = decmsg 
     
    15501645                                error_msg = msgtxt 
    15511646                                msgtxt = None 
    1552                         if self.name not in no_log_for: 
     1647                        if session.is_loggable(): 
    15531648                                try: 
    15541649                                        gajim.logger.write('error', frm, error_msg, tim = tim, 
     
    15661661                                self.dispatch('GC_SUBJECT', (frm, subject, msgtxt, has_timestamp)) 
    15671662                        else: 
     1663                                statusCode = msg.getStatusCode() 
    15681664                                if not msg.getTag('body'): #no <body> 
    15691665                                        # It could be a config change. See 
    15701666                                        # http://www.xmpp.org/extensions/xep-0045.html#roomconfig-notify 
    15711667                                        if msg.getTag('x'): 
    1572                                                 statusCode = msg.getStatusCode() 
    15731668                                                if statusCode != []: 
    15741669                                                        self.dispatch('GC_CONFIG_CHANGE', (jid, statusCode)) 
     
    15771672                                if not self.last_history_line.has_key(jid): 
    15781673                                        return 
    1579                                 self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml)) 
    1580                                 if self.name not in no_log_for and not int(float(mktime(tim)))\ 
    1581                                 <= self.last_history_line[jid] and msgtxt: 
     1674                                self.dispatch('GC_MSG', (frm, msgtxt, tim, has_timestamp, msghtml, 
     1675                                        statusCode)) 
     1676                                if self.name not in no_log_for and jid not in no_log_for and not \ 
     1677                                int(float(mktime(tim))) <= self.last_history_line[jid] and msgtxt: 
    15821678                                        try: 
    15831679                                                gajim.logger.write('gc_msg', frm, msgtxt, tim = tim) 
     
    15881684                        if not msg.getTag('body') and chatstate is None: #no <body> 
    15891685                                return 
    1590                         if msg.getTag('body') and self.name not in no_log_for and jid not in\ 
    1591                                 no_log_for and msgtxt: 
     1686                        if msg.getTag('body') and session.is_loggable() and msgtxt: 
    15921687                                try: 
    15931688                                        msg_id = gajim.logger.write('chat_msg_recv', frm, msgtxt, 
     
    16021697                                item = invite.getTag('password') 
    16031698                                password = invite.getTagData('password') 
    1604                                 self.dispatch('GC_INVITATION',(frm, jid_from, reason, password)) 
     1699                                is_continued = False 
     1700                                if invite.getTag('invite').getTag('continue'): 
     1701                                        is_continued = True 
     1702                                self.dispatch('GC_INVITATION',(frm, jid_from, reason, password, 
     1703                                        is_continued)) 
    16051704                                return 
    1606                         if self.name not in no_log_for and jid not in no_log_for and msgtxt: 
     1705                        if session.is_loggable()and msgtxt: 
    16071706                                try: 
    16081707                                        gajim.logger.write('single_msg_recv', frm, msgtxt, tim = tim, 
     
    16151714                        mtype = treat_as 
    16161715                self.dispatch('MSG', (frm, msgtxt, tim, encrypted, mtype, 
    1617                         subject, chatstate, msg_id, composing_xep, user_nick, msghtml)) 
     1716                        subject, chatstate, msg_id, composing_xep, user_nick, msghtml, 
     1717                        session, form_node)) 
    16181718        # END messageCB 
     1719 
     1720        def get_session(self, jid, thread_id, type): 
     1721                '''returns an existing session between this connection and 'jid', returns a new one if none exist.''' 
     1722                session = self.find_session(jid, thread_id, type) 
     1723 
     1724                if session: 
     1725                        return session 
     1726                else: 
     1727                        # it's possible we initiated a session with a bare JID and this is the 
     1728                        # first time we've seen a resource 
     1729                        bare_jid = gajim.get_jid_without_resource(jid) 
     1730                        if bare_jid != jid: 
     1731                                session = self.find_session(bare_jid, thread_id, type) 
     1732                                if session: 
     1733                                        if not session.received_thread_id: 
     1734                                                thread_id = session.thread_id 
     1735 
     1736                                        self.move_session(bare_jid, thread_id, jid.split("/")[1]) 
     1737                                        return session 
     1738 
     1739                return self.make_new_session(jid, thread_id, type) 
     1740 
     1741        def find_session(self, jid, thread_id, type): 
     1742                try: 
     1743                        if type == 'chat' and not thread_id: 
     1744                                return self.find_null_session(jid) 
     1745                        else: