From 0ceb41f65098ac9d9b240c60af19edde659da500 Mon Sep 17 00:00:00 2001 From: Yann Leboulanger Date: Thu, 22 Nov 2007 10:41:57 +0000 Subject: [PATCH] request GPG password only when connected to server, before sending presence. fixes #3483, #3375, #3115 --- src/common/connection.py | 79 ++++++++++++++-------------- src/common/connection_handlers.py | 85 +++++++++++++++++++------------ src/gajim.py | 40 ++++++++++++++- src/roster_window.py | 57 +++------------------ 4 files changed, 142 insertions(+), 119 deletions(-) diff --git a/src/common/connection.py b/src/common/connection.py index fdd8631ff..00128e69e 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -139,18 +139,13 @@ class Connection(ConnectionHandlers): self.blocked_contacts = [] self.blocked_groups = [] self.pep_supported = False - # Do we continue connection when we get roster (send presence,get vcard...) + # Do we continue connection when we get roster (send presence,get vcard..) self.continue_connect_info = None # To know the groupchat jid associated with a sranza ID. Useful to # request vcard or os info... to a real JID but act as if it comes from # the fake jid self.groupchat_jids = {} # {ID : groupchat_jid} - if USE_GPG: - self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) - gajim.config.set('usegpg', True) - else: - gajim.config.set('usegpg', False) - + self.on_connect_success = None self.on_connect_failure = None self.retrycount = 0 @@ -179,9 +174,8 @@ class Connection(ConnectionHandlers): self.connected = 1 self.dispatch('STATUS', 'connecting') self.retrycount += 1 - signed = self.get_signed_msg(self.status) self.on_connect_auth = self._init_roster - self.connect_and_init(self.old_show, self.status, signed) + self.connect_and_init(self.old_show, self.status, self.gpg != None) else: # reconnect succeeded self.time_to_reconnect = None @@ -539,7 +533,8 @@ class Connection(ConnectionHandlers): gajim.log.debug("Couldn't authenticate to %s" % self._hostname) self.disconnect(on_purpose = True) self.dispatch('STATUS', 'offline') - self.dispatch('ERROR', (_('Authentication failed with "%s"') % self._hostname, + self.dispatch('ERROR', (_('Authentication failed with "%s"') % \ + self._hostname, _('Please check your login and password for correctness.'))) if self.on_connect_auth: self.on_connect_auth(None) @@ -670,29 +665,32 @@ class Connection(ConnectionHandlers): self.dispatch('SIGNED_IN', ()) def test_gpg_passphrase(self, password): + if not self.gpg: + return False self.gpg.passphrase = password keyID = gajim.config.get_per('accounts', self.name, 'keyid') signed = self.gpg.sign('test', keyID) self.gpg.password = None return signed != 'BAD_PASSPHRASE' - def get_signed_msg(self, msg): + def get_signed_msg(self, msg, callback = None): + '''returns the signed message if possible + or an empty string if gpg is not used + or None if waiting for passphrase. + callback is the function to call when user give the passphrase''' signed = '' keyID = gajim.config.get_per('accounts', self.name, 'keyid') - if keyID and USE_GPG: + if keyID and self.gpg: use_gpg_agent = gajim.config.get('use_gpg_agent') - if self.connected < 2 and self.gpg.passphrase is None and \ - not use_gpg_agent: + if self.gpg.passphrase is None and not use_gpg_agent: # We didn't set a passphrase - self.dispatch('ERROR', (_('OpenPGP passphrase was not given'), - #%s is the account name here - _('You will be connected to %s without OpenPGP.') % self.name)) - elif self.gpg.passphrase is not None or use_gpg_agent: + return None + if self.gpg.passphrase is not None or use_gpg_agent: signed = self.gpg.sign(msg, keyID) if signed == 'BAD_PASSPHRASE': + self.gpg = None signed = '' - if self.connected < 2: - self.dispatch('BAD_PASSPHRASE', ()) + self.dispatch('BAD_PASSPHRASE', ()) return signed def connect_and_auth(self): @@ -700,21 +698,22 @@ class Connection(ConnectionHandlers): self.on_connect_failure = self._connect_failure self.connect() - def connect_and_init(self, show, msg, signed): - self.continue_connect_info = [show, msg, signed] + def connect_and_init(self, show, msg, signe_msg): + self.continue_connect_info = [show, msg, signe_msg] self.on_connect_auth = self._init_roster self.connect_and_auth() def _init_roster(self, con): self.connection = con - if self.connection: - con.set_send_timeout(self.keepalives, self.send_keepalive) - self.connection.onreceive(None) - iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '') - id = self.connection.getAnID() - iq.setID(id) - self.awaiting_answers[id] = (PRIVACY_ARRIVED, ) - self.connection.send(iq) + if not self.connection: + return + self.connection.set_send_timeout(self.keepalives, self.send_keepalive) + self.connection.onreceive(None) + iq = common.xmpp.Iq('get', common.xmpp.NS_PRIVACY, xmlns = '') + id = self.connection.getAnID() + iq.setID(id) + self.awaiting_answers[id] = (PRIVACY_ARRIVED, ) + self.connection.send(iq) def send_custom_status(self, show, msg, jid): if not show in STATUS_LIST: @@ -749,9 +748,9 @@ class Connection(ConnectionHandlers): if not msg: msg = '' keyID = gajim.config.get_per('accounts', self.name, 'keyid') - signed = '' + signe_msg = False if not auto and not show == 'offline': - signed = self.get_signed_msg(msg) + signe_msg = True self.status = msg if show != 'offline' and not self.connected: # set old_show to requested 'show' in case we need to @@ -766,7 +765,12 @@ class Connection(ConnectionHandlers): safe_substitute({ 'hostname': socket.gethostname() }) - self.connect_and_init(show, msg, signed) + if USE_GPG and not self.gpg: + self.gpg = GnuPG.GnuPG(gajim.config.get('use_gpg_agent')) + gajim.config.set('usegpg', True) + else: + gajim.config.set('usegpg', False) + self.connect_and_init(show, msg, signe_msg) elif show == 'offline': self.connected = 0 @@ -801,6 +805,7 @@ class Connection(ConnectionHandlers): p = self.add_sha(p) if msg: p.setStatus(msg) + signed = self.get_signed_msg(msg) if signed: p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) if self.connection: @@ -839,7 +844,7 @@ class Connection(ConnectionHandlers): fjid += '/' + resource msgtxt = msg msgenc = '' - if keyID and USE_GPG: + if keyID and self.gpg: #encrypt msgenc, error = self.gpg.encrypt(msg, [keyID]) if msgenc and not error: @@ -1377,7 +1382,7 @@ class Connection(ConnectionHandlers): self.connection.send(iq) def gpg_passphrase(self, passphrase): - if USE_GPG: + if self.gpg: use_gpg_agent = gajim.config.get('use_gpg_agent') if use_gpg_agent: self.gpg.passphrase = None @@ -1385,13 +1390,13 @@ class Connection(ConnectionHandlers): self.gpg.passphrase = passphrase def ask_gpg_keys(self): - if USE_GPG: + if self.gpg: keys = self.gpg.get_keys() return keys return None def ask_gpg_secrete_keys(self): - if USE_GPG: + if self.gpg: keys = self.gpg.get_secret_keys() return keys return None diff --git a/src/common/connection_handlers.py b/src/common/connection_handlers.py index 647c69b3d..a89c0a377 100644 --- a/src/common/connection_handlers.py +++ b/src/common/connection_handlers.py @@ -1419,7 +1419,7 @@ class ConnectionHandlers(ConnectionVcard, ConnectionBytestream, ConnectionDisco, qp.setTagData('utc', strftime('%Y%m%dT%T', gmtime())) qp.setTagData('tz', tzname[daylight]) qp.setTagData('display', helpers.decode_string(strftime('%c', - localtime()))) + localtime())) self.connection.send(iq_obj) raise common.xmpp.NodeProcessed @@ -2129,41 +2129,62 @@ returns the session that we last sent a message to.''' # continue connection if self.connected > 1 and self.continue_connect_info: - show = self.continue_connect_info[0] - msg = self.continue_connect_info[1] - signed = self.continue_connect_info[2] - self.connected = STATUS_LIST.index(show) - sshow = helpers.get_xmpp_show(show) - # send our presence - if show == 'invisible': - self.send_invisible_presence(msg, signed, True) - return - priority = gajim.get_priority(self.name, sshow) - vcard = self.get_cached_vcard(jid) - if vcard and vcard.has_key('PHOTO') and vcard['PHOTO'].has_key('SHA'): - self.vcard_sha = vcard['PHOTO']['SHA'] - p = common.xmpp.Presence(typ = None, priority = priority, show = sshow) - p = self.add_sha(p) - if msg: - p.setStatus(msg) - if signed: - p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) + to_be_signed = self.continue_connect_info[2] + signed = '' + if to_be_signed: + signed = self.get_signed_msg(to_be_signed, + self._send_first_presence) + if signed is None: + self.dispatch('GPG_PASSWORD_REQUIRED', + (self._send_first_presence,)) + # _send_first_presence will be called when user enter passphrase + return + self._send_first_presence(signed) - if self.connection: - self.connection.send(p) - self.priority = priority - self.dispatch('STATUS', show) - # ask our VCard - self.request_vcard(None) + def _send_first_presence(self, signed = ''): + show = self.continue_connect_info[0] + msg = self.continue_connect_info[1] + to_be_signed = self.continue_connect_info[2] + if to_be_signed and not signed: + signed = self.get_signed_msg(self.continue_connect_info[2]) + if signed is None: + self.dispatch('ERROR', (_('OpenPGP passphrase was not given'), + #%s is the account name here + _('You will be connected to %s without OpenPGP.') % self.name)) + signed = '' + self.connected = STATUS_LIST.index(show) + sshow = helpers.get_xmpp_show(show) + # send our presence + if show == 'invisible': + self.send_invisible_presence(msg, signed, True) + return + priority = gajim.get_priority(self.name, sshow) + our_jid = helpers.parse_jid(gajim.get_jid_from_account(self.name)) + vcard = self.get_cached_vcard(our_jid) + if vcard and vcard.has_key('PHOTO') and vcard['PHOTO'].has_key('SHA'): + self.vcard_sha = vcard['PHOTO']['SHA'] + p = common.xmpp.Presence(typ = None, priority = priority, show = sshow) + p = self.add_sha(p) + if msg: + p.setStatus(msg) + if signed: + p.setTag(common.xmpp.NS_SIGNED + ' x').setData(signed) - # Get bookmarks from private namespace - self.get_bookmarks() + if self.connection: + self.connection.send(p) + self.priority = priority + self.dispatch('STATUS', show) + # ask our VCard + self.request_vcard(None) - # Get annotations from private namespace - self.get_annotations() + # Get bookmarks from private namespace + self.get_bookmarks() - #Inform GUI we just signed in - self.dispatch('SIGNED_IN', ()) + # Get annotations from private namespace + self.get_annotations() + + # Inform GUI we just signed in + self.dispatch('SIGNED_IN', ()) self.continue_connect_info = None def request_gmail_notifications(self): diff --git a/src/gajim.py b/src/gajim.py index 8ada72ccd..a4a56a35d 100755 --- a/src/gajim.py +++ b/src/gajim.py @@ -1365,15 +1365,51 @@ class Interface: notify.popup(event_type, jid, account, 'gc-invitation', path, event_type, room_jid) + def forget_gpg_passphrase(self, keyid): + if self.gpg_passphrase.has_key(keyid): + del self.gpg_passphrase[keyid] + return False + def handle_event_bad_passphrase(self, account, array): use_gpg_agent = gajim.config.get('use_gpg_agent') if use_gpg_agent: return keyID = gajim.config.get_per('accounts', account, 'keyid') - self.roster.forget_gpg_passphrase(keyID) + self.forget_gpg_passphrase(keyID) dialogs.WarningDialog(_('Your passphrase is incorrect'), _('You are currently connected without your OpenPGP key.')) + def handle_event_gpg_password_required(self, account, array): + #('GPG_PASSWORD_REQUIRED', account, (callback,)) + callback = array[0] + keyid = gajim.config.get_per('accounts', account, 'keyid') + if self.gpg_passphrase.has_key(keyid): + gajim.connections[account].gpg_passphrase(self.gpg_passphrase[keyid]) + callback() + return + password_ok = False + count = 0 + title = _('Passphrase Required') + second = _('Enter GPG key passphrase for account %s.') % account + while not password_ok and count < 3: + count += 1 + w = dialogs.PassphraseDialog(title, second, '') + passphrase, save = w.run() + if passphrase == -1: + # User pressed cancel + passphrase = None + password_ok = True + else: + password_ok = gajim.connections[account].\ + test_gpg_passphrase(passphrase) + title = _('Wrong Passphrase') + second = _('Please retype your GPG passphrase or press Cancel.') + if passphrase != None: + self.gpg_passphrase[keyid] = passphrase + gobject.timeout_add(30000, self.forget_gpg_passphrase, keyid) + gajim.connections[account].gpg_passphrase(passphrase) + callback() + def handle_event_roster_info(self, account, array): #('ROSTER_INFO', account, (jid, name, sub, ask, groups)) jid = array[0] @@ -2463,6 +2499,7 @@ class Interface: self.handle_event_unique_room_id_unsupported, 'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported, 'SESSION_NEG': self.handle_session_negotiation, + 'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required, } gajim.handlers = self.handlers @@ -2578,6 +2615,7 @@ class Interface: self.minimized_controls = {} self.status_sent_to_users = {} self.status_sent_to_groups = {} + self.gpg_passphrase = {} self.default_colors = { 'inmsgcolor': gajim.config.get('inmsgcolor'), 'outmsgcolor': gajim.config.get('outmsgcolor'), diff --git a/src/roster_window.py b/src/roster_window.py index 18ff203ee..33f54f50d 100644 --- a/src/roster_window.py +++ b/src/roster_window.py @@ -1498,9 +1498,10 @@ class RosterWindow: name = account_name, show = connection.get_status(), sub = '', status = connection.status, resource = connection.server_resource, - priority = connection.priority, - keyID = gajim.config.get_per('accounts', connection.name, - 'keyid')) + priority = connection.priority) + if gajim.connections[account].gpg: + contact.keyID = gajim.config.get_per('accounts', connection.name, + 'keyid') contacts.append(contact) # if we're online ... if connection.connection: @@ -3426,11 +3427,6 @@ class RosterWindow: on_response_ok = (on_ok2, list_)) - def forget_gpg_passphrase(self, keyid): - if self.gpg_passphrase.has_key(keyid): - del self.gpg_passphrase[keyid] - return False - def set_connecting_state(self, account): model = self.tree.get_model() accountIter = self.get_account_iter(account) @@ -3468,46 +3464,10 @@ class RosterWindow: gajim.config.set_per('accounts', account, 'savepass', True) passwords.save_password(account, passphrase) - keyid = None - use_gpg_agent = gajim.config.get('use_gpg_agent') - # we don't need to bother with the passphrase if we use the agent - keyid = gajim.config.get_per('accounts', account, 'keyid') - if keyid and not gajim.config.get('usegpg'): - dialog = dialogs.WarningDialog(_('GPG is not usable'), _('You will be connected to %s without OpenPGP.') % account) - if keyid and gajim.connections[account].connected < 2 and \ - gajim.config.get('usegpg'): - - if use_gpg_agent: - self.gpg_passphrase[keyid] = None - else: - if self.gpg_passphrase.has_key(keyid): - passphrase = self.gpg_passphrase[keyid] - save = False - else: - password_ok = False - count = 0 - title = _('Passphrase Required') - second = _('Enter GPG key passphrase for account %s.') % \ - account - while not password_ok and count < 3: - count += 1 - w = dialogs.PassphraseDialog(title, second, - _('Save passphrase')) - passphrase, save = w.run() - if passphrase == -1: - passphrase = None - password_ok = True - else: - password_ok = gajim.connections[account].\ - test_gpg_passphrase(passphrase) - title = _('Wrong Passphrase') - second = _('Please retype your GPG passphrase or ' - 'press Cancel.') - if passphrase != None: - self.gpg_passphrase[keyid] = passphrase - gobject.timeout_add(30000, self.forget_gpg_passphrase, - keyid) - gajim.connections[account].gpg_passphrase(passphrase) + keyid = gajim.config.get_per('accounts', account, 'keyid') + if keyid and not common.connection.USE_GPG: + dialog = dialogs.WarningDialog(_('GPG is not usable'), + _('You will be connected to %s without OpenPGP.') % account) if gajim.account_is_connected(account): if status == 'online' and gajim.interface.sleeper.getState() != \ @@ -5302,7 +5262,6 @@ class RosterWindow: self.popups_notification_height = 0 self.popup_notification_windows = [] - self.gpg_passphrase = {} #(icon, name, type, jid, account, editable, secondary_pixbuf) model = gtk.TreeStore(gtk.Image, str, str, str, str, gtk.gdk.Pixbuf)