request connection password only when neede. No need to request it for GSSAPI or ANONYMOUS login. see #2465
This commit is contained in:
parent
95d7e157da
commit
849108b11f
|
@ -167,6 +167,7 @@ class Connection(ConnectionHandlers):
|
||||||
# request vcard or os info... to a real JID but act as if it comes from
|
# request vcard or os info... to a real JID but act as if it comes from
|
||||||
# the fake jid
|
# the fake jid
|
||||||
self.groupchat_jids = {} # {ID : groupchat_jid}
|
self.groupchat_jids = {} # {ID : groupchat_jid}
|
||||||
|
self.pasword_callback = None
|
||||||
|
|
||||||
self.on_connect_success = None
|
self.on_connect_success = None
|
||||||
self.on_connect_failure = None
|
self.on_connect_failure = None
|
||||||
|
@ -1815,6 +1816,19 @@ class Connection(ConnectionHandlers):
|
||||||
q.setTagData('password',password)
|
q.setTagData('password',password)
|
||||||
self.connection.send(iq)
|
self.connection.send(iq)
|
||||||
|
|
||||||
|
def get_password(self, callback):
|
||||||
|
if self.password:
|
||||||
|
callback(self.password)
|
||||||
|
return
|
||||||
|
self.pasword_callback = callback
|
||||||
|
self.dispatch('PASSWORD_REQUIRED', None)
|
||||||
|
|
||||||
|
def set_password(self, password):
|
||||||
|
self.password = password
|
||||||
|
if self.pasword_callback:
|
||||||
|
self.pasword_callback(password)
|
||||||
|
self.pasword_callback = None
|
||||||
|
|
||||||
def unregister_account(self, on_remove_success):
|
def unregister_account(self, on_remove_success):
|
||||||
# no need to write this as a class method and keep the value of
|
# no need to write this as a class method and keep the value of
|
||||||
# on_remove_success as a class property as pass it as an argument
|
# on_remove_success as a class property as pass it as an argument
|
||||||
|
|
|
@ -212,13 +212,8 @@ class SASL(PlugIn):
|
||||||
self.mechanism = 'DIGEST-MD5'
|
self.mechanism = 'DIGEST-MD5'
|
||||||
elif 'PLAIN' in self.mecs:
|
elif 'PLAIN' in self.mecs:
|
||||||
self.mecs.remove('PLAIN')
|
self.mecs.remove('PLAIN')
|
||||||
sasl_data = u'%s\x00%s\x00%s' % (self.username + '@' + \
|
|
||||||
self._owner.Server, self.username, self.password)
|
|
||||||
sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
|
|
||||||
'\n','')
|
|
||||||
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
|
|
||||||
payload=[sasl_data])
|
|
||||||
self.mechanism = 'PLAIN'
|
self.mechanism = 'PLAIN'
|
||||||
|
self._owner._caller.get_password(self.set_password)
|
||||||
else:
|
else:
|
||||||
self.startsasl = SASL_FAILURE
|
self.startsasl = SASL_FAILURE
|
||||||
log.error('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.')
|
log.error('I can only use DIGEST-MD5, GSSAPI and PLAIN mecanisms.')
|
||||||
|
@ -297,36 +292,21 @@ class SASL(PlugIn):
|
||||||
if 'qop' in chal and ((isinstance(chal['qop'], str) and \
|
if 'qop' in chal and ((isinstance(chal['qop'], str) and \
|
||||||
chal['qop'] =='auth') or (isinstance(chal['qop'], list) and 'auth' in \
|
chal['qop'] =='auth') or (isinstance(chal['qop'], list) and 'auth' in \
|
||||||
chal['qop'])):
|
chal['qop'])):
|
||||||
resp = {}
|
self.resp = {}
|
||||||
resp['username'] = self.username
|
self.resp['username'] = self.username
|
||||||
if self.realm:
|
if self.realm:
|
||||||
resp['realm'] = self.realm
|
self.resp['realm'] = self.realm
|
||||||
else:
|
else:
|
||||||
resp['realm'] = self._owner.Server
|
self.resp['realm'] = self._owner.Server
|
||||||
resp['nonce'] = chal['nonce']
|
self.resp['nonce'] = chal['nonce']
|
||||||
resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in
|
self.resp['cnonce'] = ''.join("%x" % randint(0, 2**28) for randint in
|
||||||
itertools.repeat(random.randint, 7))
|
itertools.repeat(random.randint, 7))
|
||||||
resp['nc'] = ('00000001')
|
self.resp['nc'] = ('00000001')
|
||||||
resp['qop'] = 'auth'
|
self.resp['qop'] = 'auth'
|
||||||
resp['digest-uri'] = 'xmpp/' + self._owner.Server
|
self.resp['digest-uri'] = 'xmpp/' + self._owner.Server
|
||||||
A1=C([H(C([resp['username'], resp['realm'], self.password])),
|
self.resp['charset'] = 'utf-8'
|
||||||
resp['nonce'], resp['cnonce']])
|
# Password is now required
|
||||||
A2=C(['AUTHENTICATE',resp['digest-uri']])
|
self._owner._caller.get_password(self.set_password)
|
||||||
response= HH(C([HH(A1), resp['nonce'], resp['nc'], resp['cnonce'],
|
|
||||||
resp['qop'], HH(A2)]))
|
|
||||||
resp['response'] = response
|
|
||||||
resp['charset'] = 'utf-8'
|
|
||||||
sasl_data = u''
|
|
||||||
for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce',
|
|
||||||
'digest-uri', 'response', 'qop'):
|
|
||||||
if key in ('nc','qop','response','charset'):
|
|
||||||
sasl_data += u"%s=%s," % (key, resp[key])
|
|
||||||
else:
|
|
||||||
sasl_data += u'%s="%s",' % (key, resp[key])
|
|
||||||
sasl_data = sasl_data[:-1].encode('utf-8').encode('base64').replace(
|
|
||||||
'\r','').replace('\n','')
|
|
||||||
node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data])
|
|
||||||
self._owner.send(str(node))
|
|
||||||
elif 'rspauth' in chal:
|
elif 'rspauth' in chal:
|
||||||
self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL})))
|
self._owner.send(str(Node('response', attrs={'xmlns':NS_SASL})))
|
||||||
else:
|
else:
|
||||||
|
@ -335,6 +315,34 @@ class SASL(PlugIn):
|
||||||
if self.on_sasl:
|
if self.on_sasl:
|
||||||
self.on_sasl()
|
self.on_sasl()
|
||||||
raise NodeProcessed
|
raise NodeProcessed
|
||||||
|
|
||||||
|
def set_password(self, password):
|
||||||
|
self.password = password
|
||||||
|
if self.mechanism == 'DIGEST-MD5':
|
||||||
|
A1 = C([H(C([self.resp['username'], self.resp['realm'],
|
||||||
|
self.password])), self.resp['nonce'], self.resp['cnonce']])
|
||||||
|
A2 = C(['AUTHENTICATE', self.resp['digest-uri']])
|
||||||
|
response= HH(C([HH(A1), self.resp['nonce'], self.resp['nc'],
|
||||||
|
self.resp['cnonce'], self.resp['qop'], HH(A2)]))
|
||||||
|
self.resp['response'] = response
|
||||||
|
sasl_data = u''
|
||||||
|
for key in ('charset', 'username', 'realm', 'nonce', 'nc', 'cnonce',
|
||||||
|
'digest-uri', 'response', 'qop'):
|
||||||
|
if key in ('nc','qop','response','charset'):
|
||||||
|
sasl_data += u"%s=%s," % (key, self.resp[key])
|
||||||
|
else:
|
||||||
|
sasl_data += u'%s="%s",' % (key, self.resp[key])
|
||||||
|
sasl_data = sasl_data[:-1].encode('utf-8').encode('base64').replace(
|
||||||
|
'\r', '').replace('\n', '')
|
||||||
|
node = Node('response', attrs={'xmlns':NS_SASL}, payload=[sasl_data])
|
||||||
|
elif self.mechanism == 'PLAIN':
|
||||||
|
sasl_data = u'%s\x00%s\x00%s' % (self.username + '@' + \
|
||||||
|
self._owner.Server, self.username, self.password)
|
||||||
|
sasl_data = sasl_data.encode('utf-8').encode('base64').replace(
|
||||||
|
'\n', '')
|
||||||
|
node = Node('auth', attrs={'xmlns': NS_SASL, 'mechanism': 'PLAIN'},
|
||||||
|
payload=[sasl_data])
|
||||||
|
self._owner.send(str(node))
|
||||||
|
|
||||||
|
|
||||||
class NonBlockingNonSASL(PlugIn):
|
class NonBlockingNonSASL(PlugIn):
|
||||||
|
|
24
src/gajim.py
24
src/gajim.py
|
@ -265,6 +265,7 @@ from common import socks5
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common import optparser
|
from common import optparser
|
||||||
from common import dataforms
|
from common import dataforms
|
||||||
|
from common import passwords
|
||||||
|
|
||||||
if verbose: gajim.verbose = True
|
if verbose: gajim.verbose = True
|
||||||
del verbose
|
del verbose
|
||||||
|
@ -1522,6 +1523,28 @@ class Interface:
|
||||||
self.gpg_passphrase[keyid] = request
|
self.gpg_passphrase[keyid] = request
|
||||||
request.add_callback(account, callback)
|
request.add_callback(account, callback)
|
||||||
|
|
||||||
|
def handle_event_password_required(self, account, array):
|
||||||
|
#('PASSWORD_REQUIRED', account, None)
|
||||||
|
text = _('Enter your password for account %s') % account
|
||||||
|
if passwords.USER_HAS_GNOMEKEYRING and \
|
||||||
|
not passwords.USER_USES_GNOMEKEYRING:
|
||||||
|
text += '\n' + _('Gnome Keyring is installed but not \
|
||||||
|
correctly started (environment variable probably not \
|
||||||
|
correctly set)')
|
||||||
|
|
||||||
|
def on_ok(passphrase, save):
|
||||||
|
if save:
|
||||||
|
gajim.config.set_per('accounts', account, 'savepass', True)
|
||||||
|
passwords.save_password(account, passphrase)
|
||||||
|
gajim.connections[account].set_password(passphrase)
|
||||||
|
|
||||||
|
def on_cancel():
|
||||||
|
self.roster.set_state(account, 'offline')
|
||||||
|
self.roster.update_status_combobox()
|
||||||
|
|
||||||
|
dialogs.PassphraseDialog(_('Password Required'), text, _('Save password'),
|
||||||
|
ok_handler=on_ok, cancel_handler=on_cancel)
|
||||||
|
|
||||||
def handle_event_roster_info(self, account, array):
|
def handle_event_roster_info(self, account, array):
|
||||||
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
|
#('ROSTER_INFO', account, (jid, name, sub, ask, groups))
|
||||||
jid = array[0]
|
jid = array[0]
|
||||||
|
@ -2262,6 +2285,7 @@ class Interface:
|
||||||
self.handle_event_unique_room_id_unsupported,
|
self.handle_event_unique_room_id_unsupported,
|
||||||
'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported,
|
'UNIQUE_ROOM_ID_SUPPORTED': self.handle_event_unique_room_id_supported,
|
||||||
'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required,
|
'GPG_PASSWORD_REQUIRED': self.handle_event_gpg_password_required,
|
||||||
|
'PASSWORD_REQUIRED': self.handle_event_password_required,
|
||||||
'SSL_ERROR': self.handle_event_ssl_error,
|
'SSL_ERROR': self.handle_event_ssl_error,
|
||||||
'FINGERPRINT_ERROR': self.handle_event_fingerprint_error,
|
'FINGERPRINT_ERROR': self.handle_event_fingerprint_error,
|
||||||
'PLAIN_CONNECTION': self.handle_event_plain_connection,
|
'PLAIN_CONNECTION': self.handle_event_plain_connection,
|
||||||
|
|
|
@ -54,7 +54,6 @@ import features_window
|
||||||
|
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common import passwords
|
|
||||||
from common.exceptions import GajimGeneralException
|
from common.exceptions import GajimGeneralException
|
||||||
from common import i18n
|
from common import i18n
|
||||||
from common import pep
|
from common import pep
|
||||||
|
@ -1909,13 +1908,16 @@ class RosterWindow:
|
||||||
dialogs.InformationDialog(_('Authorization has been removed'),
|
dialogs.InformationDialog(_('Authorization has been removed'),
|
||||||
_('Now "%s" will always see you as offline.') %jid)
|
_('Now "%s" will always see you as offline.') %jid)
|
||||||
|
|
||||||
def set_connecting_state(self, account):
|
def set_state(self, account, state):
|
||||||
child_iterA = self._get_account_iter(account, self.model)
|
child_iterA = self._get_account_iter(account, self.model)
|
||||||
if child_iterA:
|
if child_iterA:
|
||||||
self.model[child_iterA][0] = \
|
self.model[child_iterA][0] = \
|
||||||
gajim.interface.jabber_state_images['16']['connecting']
|
gajim.interface.jabber_state_images['16'][state]
|
||||||
if gajim.interface.systray_enabled:
|
if gajim.interface.systray_enabled:
|
||||||
gajim.interface.systray.change_status('connecting')
|
gajim.interface.systray.change_status(state)
|
||||||
|
|
||||||
|
def set_connecting_state(self, account):
|
||||||
|
self.set_state(account, 'connecting')
|
||||||
|
|
||||||
def send_status(self, account, status, txt, auto=False, to=None):
|
def send_status(self, account, status, txt, auto=False, to=None):
|
||||||
child_iterA = self._get_account_iter(account, self.model)
|
child_iterA = self._get_account_iter(account, self.model)
|
||||||
|
@ -1927,38 +1929,6 @@ class RosterWindow:
|
||||||
if gajim.connections[account].connected < 2:
|
if gajim.connections[account].connected < 2:
|
||||||
self.set_connecting_state(account)
|
self.set_connecting_state(account)
|
||||||
|
|
||||||
if not gajim.connections[account].password:
|
|
||||||
text = _('Enter your password for account %s') % account
|
|
||||||
if passwords.USER_HAS_GNOMEKEYRING and \
|
|
||||||
not passwords.USER_USES_GNOMEKEYRING:
|
|
||||||
text += '\n' + _('Gnome Keyring is installed but not \
|
|
||||||
correctly started (environment variable probably not \
|
|
||||||
correctly set)')
|
|
||||||
def on_ok(passphrase, save):
|
|
||||||
gajim.connections[account].password = passphrase
|
|
||||||
if save:
|
|
||||||
gajim.config.set_per('accounts', account, 'savepass', True)
|
|
||||||
passwords.save_password(account, passphrase)
|
|
||||||
keyid = gajim.config.get_per('accounts', account, 'keyid')
|
|
||||||
if keyid and not gajim.connections[account].gpg:
|
|
||||||
dialogs.WarningDialog(_('GPG is not usable'),
|
|
||||||
_('You will be connected to %s without OpenPGP.') % \
|
|
||||||
account)
|
|
||||||
self.send_status_continue(account, status, txt, auto, to)
|
|
||||||
|
|
||||||
def on_cancel():
|
|
||||||
if child_iterA:
|
|
||||||
self.model[child_iterA][0] = \
|
|
||||||
gajim.interface.jabber_state_images['16']['offline']
|
|
||||||
if gajim.interface.systray_enabled:
|
|
||||||
gajim.interface.systray.change_status('offline')
|
|
||||||
self.update_status_combobox()
|
|
||||||
|
|
||||||
dialogs.PassphraseDialog(_('Password Required'), text,
|
|
||||||
_('Save password'), ok_handler=on_ok,
|
|
||||||
cancel_handler=on_cancel)
|
|
||||||
return
|
|
||||||
|
|
||||||
keyid = gajim.config.get_per('accounts', account, 'keyid')
|
keyid = gajim.config.get_per('accounts', account, 'keyid')
|
||||||
if keyid and not gajim.connections[account].gpg:
|
if keyid and not gajim.connections[account].gpg:
|
||||||
dialogs.WarningDialog(_('GPG is not usable'),
|
dialogs.WarningDialog(_('GPG is not usable'),
|
||||||
|
|
Loading…
Reference in New Issue