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.py

    r8487 r9123  
    33## 
    44## Copyright (C) 2003-2004 Vincent Hanquez <tab@snarc.org> 
    5 ## Copyright (C) 2003-2006 Yann Le Boulanger <asterix@lagaule.org> 
     5## Copyright (C) 2003-2007 Yann Leboulanger <asterix@lagaule.org> 
    66## Copyright (C) 2005-2006 Nikos Kouremenos <kourem@gmail.com> 
    7 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov@gmail.com> 
    8 ## Copyright (C) 2005-2006 Travis Shirk <travis@pobox.com> 
     7##                         Dimitur Kirov <dkirov@gmail.com> 
     8##                         Travis Shirk <travis@pobox.com> 
    99## Copyright (C) 2007 Julien Pivotto <roidelapluie@gmail.com> 
     10##                    Stephan Erb <steve-e@h3c.de> 
    1011## 
    11 ## This program is free software; you can redistribute it and/or modify 
     12## This file is part of Gajim. 
     13## 
     14## Gajim is free software; you can redistribute it and/or modify 
    1215## it under the terms of the GNU General Public License as published 
    13 ## by the Free Software Foundation; version 2 only. 
     16## by the Free Software Foundation; version 3 only. 
    1417## 
    15 ## This program is distributed in the hope that it will be useful, 
     18## Gajim is distributed in the hope that it will be useful, 
    1619## but WITHOUT ANY WARRANTY; without even the implied warranty of 
    1720## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
    1821## GNU General Public License for more details. 
    1922## 
     23## You should have received a copy of the GNU General Public License 
     24## along with Gajim.  If not, see <http://www.gnu.org/licenses/>. 
     25## 
    2026 
    2127import os 
    2228import random 
    2329import socket 
     30 
     31import time 
    2432 
    2533try: 
     
    5159import gtkgui_helpers 
    5260 
    53 ssl_error = {  
    54 2: "Unable to get issuer certificate", 
    55 3: "Unable to get certificate CRL", 
    56 4: "Unable to decrypt certificate's signature", 
    57 5: "Unable to decrypt CRL's signature", 
    58 6: "Unable to decode issuer public key", 
    59 7: "Certificate signature failure", 
    60 8: "CRL signature failure", 
    61 9: "Certificate is not yet valid", 
    62 10: "Certificate has expired", 
    63 11: "CRL is not yet valid", 
    64 12: "CRL has expired", 
    65 13: "Format error in certificate's notBefore field", 
    66 14: "Format error in certificate's notAfter field", 
    67 15: "Format error in CRL's lastUpdate field", 
    68 16: "Format error in CRL's nextUpdate field", 
    69 17: "Out of memory", 
    70 18: "Self signed certificate in certificate chain", 
    71 19: "Unable to get local issuer certificate", 
    72 20: "Unable to verify the first certificate", 
    73 21: "Unable to verify the first certificate", 
    74 22: "Certificate chain too long", 
    75 23: "Certificate revoked", 
    76 24: "Invalid CA certificate", 
    77 25: "Path length constraint exceeded", 
    78 26: "Unsupported certificate purpose", 
    79 27: "Certificate not trusted", 
    80 28: "Certificate rejected", 
    81 29: "Subject issuer mismatch", 
    82 30: "Authority and subject key identifier mismatch", 
    83 31: "Authority and issuer serial number mismatch", 
    84 32: "Key usage does not include certificate signing", 
    85 50: "Application verification failure" 
     61ssl_error = { 
     622: _("Unable to get issuer certificate"), 
     633: _("Unable to get certificate CRL"), 
     644: _("Unable to decrypt certificate's signature"), 
     655: _("Unable to decrypt CRL's signature"), 
     666: _("Unable to decode issuer public key"), 
     677: _("Certificate signature failure"), 
     688: _("CRL signature failure"), 
     699: _("Certificate is not yet valid"), 
     7010: _("Certificate has expired"), 
     7111: _("CRL is not yet valid"), 
     7212: _("CRL has expired"), 
     7313: _("Format error in certificate's notBefore field"), 
     7414: _("Format error in certificate's notAfter field"), 
     7515: _("Format error in CRL's lastUpdate field"), 
     7616: _("Format error in CRL's nextUpdate field"), 
     7717: _("Out of memory"), 
     7818: _("Self signed certificate in certificate chain"), 
     7919: _("Unable to get local issuer certificate"), 
     8020: _("Unable to verify the first certificate"), 
     8121: _("Unable to verify the first certificate"), 
     8222: _("Certificate chain too long"), 
     8323: _("Certificate revoked"), 
     8424: _("Invalid CA certificate"), 
     8525: _("Path length constraint exceeded"), 
     8626: _("Unsupported certificate purpose"), 
     8727: _("Certificate not trusted"), 
     8828: _("Certificate rejected"), 
     8929: _("Subject issuer mismatch"), 
     9030: _("Authority and subject key identifier mismatch"), 
     9131: _("Authority and issuer serial number mismatch"), 
     9232: _("Key usage does not include certificate signing"), 
     9350: _("Application verification failure") 
    8694} 
    8795class Connection(ConnectionHandlers): 
     
    100108                self.is_zeroconf = False 
    101109                self.gpg = None 
     110                if USE_GPG: 
     111                        self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) 
    102112                self.status = '' 
    103113                self.priority = gajim.get_priority(name, 'offline') 
     
    110120                self.last_time_to_reconnect = None 
    111121                self.new_account_info = None 
     122                self.new_account_form = None 
    112123                self.bookmarks = [] 
    113124                self.annotations = {} 
     
    118129                self.password = passwords.get_password(name) 
    119130                self.server_resource = gajim.config.get_per('accounts', name, 'resource') 
    120                 # All valid resource substitution strings should be added to this hash.  
     131                # All valid resource substitution strings should be added to this hash. 
    121132                if self.server_resource: 
    122133                        self.server_resource = Template(self.server_resource).safe_substitute({ 
     
    132143                self.blocked_groups = [] 
    133144                self.pep_supported = False 
    134                 # Do we continue connection when we get roster (send presence,get vcard...) 
     145                # Do we continue connection when we get roster (send presence,get vcard..) 
    135146                self.continue_connect_info = None 
    136147                # To know the groupchat jid associated with a sranza ID. Useful to 
     
    138149                # the fake jid 
    139150                self.groupchat_jids = {} # {ID : groupchat_jid} 
    140                 if USE_GPG: 
    141                         self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) 
    142                         gajim.config.set('usegpg', True) 
    143                 else: 
    144                         gajim.config.set('usegpg', False) 
    145                  
     151 
    146152                self.on_connect_success = None 
    147153                self.on_connect_failure = None 
     
    152158                # server {'icq': ['icq.server.com', 'icq2.server.com'], } 
    153159                self.vcard_supported = True 
    154                 self.metacontacts_supported = True 
     160                self.private_storage_supported = True 
    155161        # END __init__ 
    156162 
     
    172178                        self.dispatch('STATUS', 'connecting') 
    173179                        self.retrycount += 1 
    174                         signed = self.get_signed_msg(self.status) 
    175180                        self.on_connect_auth = self._init_roster 
    176                         self.connect_and_init(self.old_show, self.status, signed) 
     181                        self.connect_and_init(self.old_show, self.status, self.gpg != None) 
    177182                else: 
    178183                        # reconnect succeeded 
    179184                        self.time_to_reconnect = None 
    180185                        self.retrycount = 0 
    181          
     186 
    182187        # We are doing disconnect at so many places, better use one function in all 
    183         def disconnect(self, on_purpose = False): 
     188        def disconnect(self, on_purpose=False): 
    184189                #FIXME: set the Tune to None before disconnection per account 
    185190                #gajim.interface.roster._music_track_changed(None, None) 
     
    194199                        self.last_connection = None 
    195200                        self.connection = None 
    196          
     201 
    197202        def _disconnectedReconnCB(self): 
    198203                '''Called when we are disconnected''' 
     
    203208                        self.old_show = STATUS_LIST[self.connected] 
    204209                self.connected = 0 
    205                 self.dispatch('STATUS', 'offline') 
    206210                if not self.on_purpose: 
     211                        self.dispatch('STATUS', 'offline') 
    207212                        self.disconnect() 
    208213                        if gajim.config.get_per('accounts', self.name, 'autoreconnect'): 
     
    236241                self.on_purpose = False 
    237242        # END disconenctedReconnCB 
    238          
     243 
    239244        def _connection_lost(self): 
    240245                self.disconnect(on_purpose = False) 
     
    258263                                        is_form = data[2] 
    259264                                        conf = data[1] 
    260                                         self.dispatch('NEW_ACC_CONNECTED', (conf, is_form)) 
     265                                        if self.new_account_form: 
     266                                                def _on_register_result(result): 
     267                                                        if not common.xmpp.isResultNode(result): 
     268                                                                self.dispatch('ACC_NOT_OK', (result.getError())) 
     269                                                                return 
     270                                                        if USE_GPG: 
     271                                                                self.gpg = GnuPG.GnuPG(gajim.config.get( 
     272                                                                        'use_gpg_agent')) 
     273                                                        self.dispatch('ACC_OK', (self.new_account_info)) 
     274                                                        self.new_account_info = None 
     275                                                        self.new_account_form = None 
     276                                                        if self.connection: 
     277                                                                self.connection.UnregisterDisconnectHandler( 
     278                                                                        self._on_new_account) 
     279                                                        self.disconnect(on_purpose=True) 
     280                                                # it's the second time we get the form, we have info user 
     281                                                # typed, so send them 
     282                                                if is_form: 
     283                                                        #TODO: Check if form has changed 
     284                                                        iq = common.xmpp.Iq('set', common.xmpp.NS_REGISTER, to=self._hostname) 
     285                                                        iq.setTag('query').addChild(node=self.new_account_form) 
     286                                                        self.connection.SendAndCallForResponse(iq, 
     287                                                                _on_register_result) 
     288                                                else: 
     289                                                        if self.new_account_form.keys().sort() != \ 
     290                                                        conf.keys().sort(): 
     291                                                                # requested config has changed since first connection 
     292                                                                self.dispatch('ACC_NOT_OK', (_( 
     293                                                                        'Server %s provided a different registration form')\ 
     294                                                                        % data[0])) 
     295                                                                return 
     296                                                        common.xmpp.features_nb.register(self.connection, 
     297                                                                self._hostname, self.new_account_form, 
     298                                                                _on_register_result) 
     299                                                return 
     300                                        try: 
     301                                                errnum = self.connection.Connection.ssl_errnum 
     302                                        except AttributeError: 
     303                                                errnum = -1 # we don't have an errnum 
     304                                        ssl_msg = '' 
     305                                        if errnum > 0: 
     306                                                if errnum in ssl_error: 
     307                                                        ssl_msg = ssl_error[errnum] 
     308                                                else: 
     309                                                        ssl_msg = _('Unknown SSL error: %d') % errnum 
     310                                        ssl_cert = '' 
     311                                        if hasattr(self.connection.Connection, 'ssl_cert_pem'): 
     312                                                ssl_cert = self.connection.Connection.ssl_cert_pem 
     313                                        ssl_fingerprint = '' 
     314                                        if hasattr(self.connection.Connection, 'ssl_fingerprint_sha1'): 
     315                                                ssl_fingerprint = \ 
     316                                                        self.connection.Connection.ssl_fingerprint_sha1 
     317                                        self.dispatch('NEW_ACC_CONNECTED', (conf, is_form, ssl_msg, 
     318                                                ssl_cert, ssl_fingerprint)) 
     319                                        self.connection.UnregisterDisconnectHandler( 
     320                                                self._on_new_account) 
     321                                        self.disconnect(on_purpose=True) 
    261322                                        return 
    262323                                if not data[1]: # wrong answer 
     
    478539                log.debug(_('Connected to server %s:%s with %s') % (self._current_host['host'], 
    479540                        self._current_host['port'], con_type)) 
    480                 self._register_handlers(con, con_type) 
    481541 
    482542                name = gajim.config.get_per('accounts', self.name, 'name') 
     
    488548                        errnum = -1 # we don't have an errnum 
    489549                if errnum > 0: 
    490                         # FIXME: tell the user that the certificat is untrusted, and ask him what to do 
    491                         try: 
    492                                 log.warning("The authenticity of the "+hostname+" certificate could be unvalid.\nSSL Error: "+ssl_error[errnum]) 
    493                         except KeyError: 
    494                                 log.warning("Unknown SSL error: %d" % errnum) 
     550                        text = _('The authenticity of the %s certificate could be invalid.') %\ 
     551                                hostname 
     552                        if errnum in ssl_error: 
     553                                text += _('\nSSL Error: %s') % ssl_error[errnum] 
     554                        else: 
     555                                text += _('\nUnknown SSL error: %d') % errnum 
     556                        self.dispatch('SSL_ERROR', (text, con.Connection.ssl_cert_pem, 
     557                                con.Connection.ssl_fingerprint_sha1)) 
     558                        return True 
     559                if hasattr(con.Connection, 'ssl_fingerprint_sha1'): 
     560                        saved_fingerprint = gajim.config.get_per('accounts', self.name, 'ssl_fingerprint_sha1') 
     561                        if saved_fingerprint: 
     562                                # Check sha1 fingerprint 
     563                                if con.Connection.ssl_fingerprint_sha1 != saved_fingerprint: 
     564                                        self.dispatch('FINGERPRINT_ERROR', 
     565                                                (con.Connection.ssl_fingerprint_sha1,)) 
     566                                        return True 
     567                self._register_handlers(con, con_type) 
    495568                con.auth(name, self.password, self.server_resource, 1, self.__on_auth) 
    496569 
    497                 return True 
     570 
     571        def ssl_certificate_accepted(self): 
     572                name = gajim.config.get_per('accounts', self.name, 'name') 
     573                self._register_handlers(self.connection, 'ssl') 
     574                self.connection.auth(name, self.password, self.server_resource, 1, self.__on_auth) 
    498575 
    499576        def _register_handlers(self, con, con_type): 
     
    529606                                self.on_connect_auth = None 
    530607                else: 
    531                         # Forget password if needed 
    532                         if not gajim.config.get_per('accounts', self.name, 'savepass'): 
    533                                 self.password = None 
     608                        # Forget password, it's wrong 
     609                        self.password = None 
    534610                        gajim.log.debug("Couldn't authenticate to %s" % self._hostname) 
    535611                        self.disconnect(on_purpose = True) 
    536612                        self.dispatch('STATUS', 'offline') 
    537                         self.dispatch('ERROR', (_('Authentication failed with "%s"') % self._hostname, 
     613                        self.dispatch('ERROR', (_('Authentication failed with "%s"') % \ 
     614                                self._hostname, 
    538615                                _('Please check your login and password for correctness.'))) 
    539616                        if self.on_connect_auth: 
     
    544621        def quit(self, kill_core): 
    545622                if kill_core and gajim.account_is_connected(self.name): 
    546                         self.disconnect(on_purpose = True) 
    547          
     623                        self.disconnect(on_purpose=True) 
     624 
    548625        def get_privacy_lists(self): 
    549626                if not self.connection: 
     
    571648                        return 
    572649                common.xmpp.features_nb.getActiveAndDefaultPrivacyLists(self.connection) 
    573          
     650 
    574651        def del_privacy_list(self, privacy_list): 
    575652                if not self.connection: 
     
    585662                common.xmpp.features_nb.delPrivacyList(self.connection, privacy_list, 
    586663                        _on_del_privacy_list_result) 
    587          
     664 
    588665        def get_privacy_list(self, title): 
    589666                if not self.connection: 
    590667                        return 
    591668                common.xmpp.features_nb.getPrivacyList(self.connection, title) 
    592          
     669 
    593670        def set_privacy_list(self, listname, tags): 
    594671                if not self.connection: 
    595672                        return 
    596673                common.xmpp.features_nb.setPrivacyList(self.connection, listname, tags) 
    597          
     674 
    598675        def set_active_list(self, listname): 
    599676                if not self.connection: 
     
    605682                        return 
    606683                common.xmpp.features_nb.setDefaultPrivacyList(self.connection, listname) 
    607          
     684 
    608685        def build_privacy_rule(self, name, action): 
    609686                '''Build a Privacy rule stanza for invisibility''' 
     
    658735                        #Get bookmarks from private namespace 
    659736                        self.get_bookmarks() 
    660                          
     737 
    661738                        #Get annotations 
    662739                        self.get_annotations() 
     
    666743 
    667744        def test_gpg_passphrase(self, password): 
     745                if not self.gpg: 
     746                        return False 
    668747                self.gpg.passphrase = password 
    669748                keyID = gajim.config.get_per('accounts', self.name, 'keyid') 
     
    672751                return signed != 'BAD_PASSPHRASE' 
    673752 
    674         def get_signed_msg(self, msg): 
     753        def get_signed_presence(self, msg, callback = None): 
     754                if gajim.config.get_per('accounts', self.name, 'gpg_sign_presence'): 
     755                        return self.get_signed_msg(msg, callback) 
     756                return '' 
     757 
     758        def get_signed_msg(self, msg, callback = None): 
     759                '''returns the signed message if possible 
     760                or an empty string if gpg is not used 
     761                or None if waiting for passphrase. 
     762                callback is the function to call when user give the passphrase''' 
    675763                signed = '' 
    676764                keyID = gajim.config.get_per('accounts', self.name, 'keyid') 
    677                 if keyID and USE_GPG: 
     765                if keyID and self.gpg: 
    678766                        use_gpg_agent = gajim.config.get('use_gpg_agent') 
    679                         if self.connected < 2 and self.gpg.passphrase is None and \ 
    680                                 not use_gpg_agent: 
     767                        if self.gpg.passphrase is None and not use_gpg_agent: 
    681768                                # We didn't set a passphrase 
    682                                 self.dispatch('ERROR', (_('OpenPGP passphrase was not given'), 
    683                                         #%s is the account name here 
    684                                         _('You will be connected to %s without OpenPGP.') % self.name)) 
    685                         elif self.gpg.passphrase is not None or use_gpg_agent: 
     769                                return None 
     770                        if self.gpg.passphrase is not None or use_gpg_agent: 
    686771                                signed = self.gpg.sign(msg, keyID) 
    687772                                if signed == 'BAD_PASSPHRASE': 
     773                                        self.gpg = None 
    688774                                        signed = '' 
    689                                         if self.connected < 2: 
    690                                                 self.dispatch('BAD_PASSPHRASE', ()) 
     775                                        self.dispatch('BAD_PASSPHRASE', ()) 
    691776                return signed 
    692777 
     
    696781                self.connect() 
    697782 
    698         def connect_and_init(self, show, msg, signed): 
    699                 self.continue_connect_info = [show, msg, signed] 
     783        def connect_and_init(self, show, msg, sign_msg): 
     784                self.continue_connect_info = [show, msg, sign_msg] 
    700785                self.on_connect_auth = self._init_roster 
    701786                self.connect_and_auth() 
     
    703788        def _init_roster(self, con): 
    704789                self.connection = con 
    705                 if self.connection: 
    706                         con.set_send_timeout(self.keepalives, self.send_keepalive) 
    707                         self.connection.onreceive(None) 
    708                         iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '') 
    709                         id = self.connection.getAnID() 
    710                         iq.setID(id) 
    711                         self.awaiting_answers[id] = (PRIVACY_ARRIVED, ) 
    712                         self.connection.send(iq) 
     790                if not self.connection: 
     791                        return 
     792                self.connection.set_send_timeout(self.keepalives, self.send_keepalive) 
     793                self.connection.onreceive(None) 
     794                iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '') 
     795                id = self.connection.getAnID() 
     796                iq.setID(id) 
     797                self.awaiting_answers[id] = (PRIVACY_ARRIVED, ) 
     798                self.connection.send(iq) 
    713799 
    714800        def send_custom_status(self, show, msg, jid): 
     
    727813                                p.setStatus(msg) 
    728814                else: 
    729                         signed = self.get_signed_msg(msg) 
     815                        signed = self.get_signed_presence(msg) 
    730816                        priority = unicode(gajim.get_priority(self.name, sshow)) 
    731817                        p = common.xmpp.Presence(typ = None, priority = priority, show = sshow, 
     
    745831                        msg = '' 
    746832                keyID = gajim.config.get_per('accounts', self.name, 'keyid') 
    747                 signed = '' 
     833                sign_msg = False 
    748834                if not auto and not show == 'offline': 
    749                         signed = self.get_signed_msg(msg) 
     835                        sign_msg = True 
    750836                self.status = msg 
    751837                if show != 'offline' and not self.connected: 
     
    753839                        # recconect before we auth to server 
    754840                        self.old_show = show 
    755                         self.on_purpose = False  
     841                        self.on_purpose = False 
    756842                        self.server_resource = gajim.config.get_per('accounts', self.name, 
    757843                                'resource') 
     
    762848                                                'hostname': socket.gethostname() 
    763849                                        }) 
    764                         self.connect_and_init(show, msg, signed) 
     850                        if USE_GPG: 
     851                                self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) 
     852                        self.connect_and_init(show, msg, sign_msg) 
    765853 
    766854                elif show == 'offline': 
     
    786874                        self.connected = STATUS_LIST.index(show) 
    787875                        if show == 'invisible': 
     876                                signed = self.get_signed_presence(msg) 
    788877                                self.send_invisible_presence(msg, signed) 
    789878                                return 
     
    797886                        if msg: 
    798887                                p.setStatus(msg) 
     888                        signed = self.get_signed_presence(msg) 
    799889                        if signed: 
    800890                                p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) 
     
    821911                self.connection.send(msg_iq) 
    822912 
    823         def send_message(self, jid, msg, keyID, type = 'chat', subject='', 
    824         chatstate = None, msg_id = None, composing_xep = None, resource = None, 
    825         user_nick = None, xhtml = None, forward_from = None): 
     913        def send_message(self, jid, msg, keyID, type='chat', subject='', 
     914        chatstate=None, msg_id=None, composing_xep=None, resource=None, 
     915        user_nick=None, xhtml=None, session=None, forward_from=None, form_node=None): 
    826916                if not self.connection: 
    827917                        return 1 
    828918                if msg and not xhtml and gajim.config.get('rst_formatting_outgoing_messages'): 
    829919                        xhtml = create_xhtml(msg) 
    830                 if not msg and chatstate is None: 
     920                if not msg and chatstate is None and form_node is None: 
    831921  &