Resolve address when lost focus.
This commit is contained in:
parent
540adeb22c
commit
090816998e
|
@ -1,21 +1,21 @@
|
||||||
# Copyright (c) 2014-2015, The Monero Project
|
# Copyright (c) 2014-2015, The Monero Project
|
||||||
#
|
#
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
#
|
#
|
||||||
# Redistribution and use in source and binary forms, with or without modification, are
|
# Redistribution and use in source and binary forms, with or without modification, are
|
||||||
# permitted provided that the following conditions are met:
|
# permitted provided that the following conditions are met:
|
||||||
#
|
#
|
||||||
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
# 1. Redistributions of source code must retain the above copyright notice, this list of
|
||||||
# conditions and the following disclaimer.
|
# conditions and the following disclaimer.
|
||||||
#
|
#
|
||||||
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
# 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
||||||
# of conditions and the following disclaimer in the documentation and/or other
|
# of conditions and the following disclaimer in the documentation and/or other
|
||||||
# materials provided with the distribution.
|
# materials provided with the distribution.
|
||||||
#
|
#
|
||||||
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
# 3. Neither the name of the copyright holder nor the names of its contributors may be
|
||||||
# used to endorse or promote products derived from this software without specific
|
# used to endorse or promote products derived from this software without specific
|
||||||
# prior written permission.
|
# prior written permission.
|
||||||
#
|
#
|
||||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||||
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
||||||
|
@ -98,6 +98,36 @@ class Plugin(BasePlugin):
|
||||||
def settings_widget(self, window):
|
def settings_widget(self, window):
|
||||||
return EnterButton(_('Settings'), self.settings_dialog)
|
return EnterButton(_('Settings'), self.settings_dialog)
|
||||||
|
|
||||||
|
@hook
|
||||||
|
def timer_actions(self):
|
||||||
|
if self.win.payto_e.hasFocus():
|
||||||
|
return
|
||||||
|
if self.win.payto_e.is_multiline(): # only supports single line entries atm
|
||||||
|
return
|
||||||
|
url = str(self.win.payto_e.toPlainText())
|
||||||
|
if url == self.win.previous_payto_e:
|
||||||
|
return
|
||||||
|
self.win.previous_payto_e = url
|
||||||
|
url = url.replace('@', '.') # support email-style addresses, per the OA standard
|
||||||
|
|
||||||
|
if ('.' in url) and (not '<' in url) and (not ' ' in url):
|
||||||
|
if not OA_READY: # handle a failed DNSPython load
|
||||||
|
QMessageBox.warning(self.win, _('Error'), 'Could not load DNSPython libraries, please ensure they are available and/or Electrum has been built correctly', _('OK'))
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
return
|
||||||
|
|
||||||
|
data = self.resolve(url)
|
||||||
|
|
||||||
|
if not data:
|
||||||
|
self.win.previous_payto_e = url
|
||||||
|
return True
|
||||||
|
|
||||||
|
(address, name) = data
|
||||||
|
new_url = url + ' <' + address + '>'
|
||||||
|
self.win.payto_e.setText(new_url)
|
||||||
|
self.win.previous_payto_e = new_url
|
||||||
|
|
||||||
@hook
|
@hook
|
||||||
def before_send(self):
|
def before_send(self):
|
||||||
'''
|
'''
|
||||||
|
@ -109,28 +139,20 @@ class Plugin(BasePlugin):
|
||||||
|
|
||||||
if self.win.payto_e.is_multiline(): # only supports single line entries atm
|
if self.win.payto_e.is_multiline(): # only supports single line entries atm
|
||||||
return False
|
return False
|
||||||
url = str(self.win.payto_e.toPlainText())
|
payto_e = str(self.win.payto_e.toPlainText())
|
||||||
|
regex = re.compile(r'^([^\s]+) <([A-Za-z0-9]+)>') # only do that for converted addresses
|
||||||
url = url.replace('@', '.') # support email-style addresses, per the OA standard
|
try:
|
||||||
|
(url, address) = regex.search(payto_e).groups()
|
||||||
if ('.' in url) and (not '<' in url) and (not ' ' in url):
|
except AttributeError:
|
||||||
if not OA_READY: # handle a failed DNSPython load
|
|
||||||
QMessageBox.warning(self.win, _('Error'), 'Could not load DNSPython libraries, please ensure they are available and/or Electrum has been built correctly', _('OK'))
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
data = self.resolve(url)
|
if not OA_READY: # handle a failed DNSPython load
|
||||||
|
QMessageBox.warning(self.win, _('Error'), 'Could not load DNSPython libraries, please ensure they are available and/or Electrum has been built correctly', _('OK'))
|
||||||
if not data:
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
(address, name) = data
|
|
||||||
self.win.payto_e.setText(url + ' <' + address + '>')
|
|
||||||
|
|
||||||
if not self.validate_dnssec(url):
|
if not self.validate_dnssec(url):
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
|
msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
|
||||||
msgBox.setInformativeText(_('Do you wish to continue?'))
|
msgBox.setInformativeText(_('Do you wish to continue?'))
|
||||||
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
|
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
|
||||||
msgBox.setDefaultButton(QMessageBox.Cancel)
|
msgBox.setDefaultButton(QMessageBox.Cancel)
|
||||||
|
@ -184,7 +206,7 @@ class Plugin(BasePlugin):
|
||||||
return
|
return
|
||||||
|
|
||||||
url = str(line1.text())
|
url = str(line1.text())
|
||||||
|
|
||||||
url = url.replace('@', '.')
|
url = url.replace('@', '.')
|
||||||
|
|
||||||
if not '.' in url:
|
if not '.' in url:
|
||||||
|
@ -200,7 +222,7 @@ class Plugin(BasePlugin):
|
||||||
|
|
||||||
if not self.validate_dnssec(url):
|
if not self.validate_dnssec(url):
|
||||||
msgBox = QMessageBox()
|
msgBox = QMessageBox()
|
||||||
msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
|
msgBox.setText(_('WARNING: the address ' + address + ' could not be validated via an additional security check, DNSSEC, and thus may not be correct.'))
|
||||||
msgBox.setInformativeText("Do you wish to continue?")
|
msgBox.setInformativeText("Do you wish to continue?")
|
||||||
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
|
msgBox.setStandardButtons(QMessageBox.Cancel | QMessageBox.Ok)
|
||||||
msgBox.setDefaultButton(QMessageBox.Cancel)
|
msgBox.setDefaultButton(QMessageBox.Cancel)
|
||||||
|
@ -243,7 +265,7 @@ class Plugin(BasePlugin):
|
||||||
def resolve(self, url):
|
def resolve(self, url):
|
||||||
'''Resolve OpenAlias address using url.'''
|
'''Resolve OpenAlias address using url.'''
|
||||||
print_msg('[OA] Attempting to resolve OpenAlias data for ' + url)
|
print_msg('[OA] Attempting to resolve OpenAlias data for ' + url)
|
||||||
|
|
||||||
prefix = 'btc'
|
prefix = 'btc'
|
||||||
retries = 3
|
retries = 3
|
||||||
err = None
|
err = None
|
||||||
|
@ -274,7 +296,7 @@ class Plugin(BasePlugin):
|
||||||
except DNSException:
|
except DNSException:
|
||||||
err = _('Unhandled exception.')
|
err = _('Unhandled exception.')
|
||||||
continue
|
continue
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
err = _('Unexpected error: ' + str(e))
|
err = _('Unexpected error: ' + str(e))
|
||||||
continue
|
continue
|
||||||
break
|
break
|
||||||
|
@ -291,52 +313,52 @@ class Plugin(BasePlugin):
|
||||||
|
|
||||||
def validate_dnssec(self, url):
|
def validate_dnssec(self, url):
|
||||||
print_msg('[OA] Checking DNSSEC trust chain for ' + url)
|
print_msg('[OA] Checking DNSSEC trust chain for ' + url)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
default = dns.resolver.get_default_resolver()
|
default = dns.resolver.get_default_resolver()
|
||||||
ns = default.nameservers[0]
|
ns = default.nameservers[0]
|
||||||
|
|
||||||
parts = url.split('.')
|
parts = url.split('.')
|
||||||
|
|
||||||
for i in xrange(len(parts), 0, -1):
|
for i in xrange(len(parts), 0, -1):
|
||||||
sub = '.'.join(parts[i - 1:])
|
sub = '.'.join(parts[i - 1:])
|
||||||
|
|
||||||
query = dns.message.make_query(sub, dns.rdatatype.NS)
|
query = dns.message.make_query(sub, dns.rdatatype.NS)
|
||||||
response = dns.query.udp(query, ns, 1)
|
response = dns.query.udp(query, ns, 1)
|
||||||
|
|
||||||
if response.rcode() != dns.rcode.NOERROR:
|
if response.rcode() != dns.rcode.NOERROR:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if len(response.authority) > 0:
|
if len(response.authority) > 0:
|
||||||
rrset = response.authority[0]
|
rrset = response.authority[0]
|
||||||
else:
|
else:
|
||||||
rrset = response.answer[0]
|
rrset = response.answer[0]
|
||||||
|
|
||||||
rr = rrset[0]
|
rr = rrset[0]
|
||||||
if rr.rdtype == dns.rdatatype.SOA:
|
if rr.rdtype == dns.rdatatype.SOA:
|
||||||
#Same server is authoritative, don't check again
|
#Same server is authoritative, don't check again
|
||||||
continue
|
continue
|
||||||
|
|
||||||
query = dns.message.make_query(sub,
|
query = dns.message.make_query(sub,
|
||||||
dns.rdatatype.DNSKEY,
|
dns.rdatatype.DNSKEY,
|
||||||
want_dnssec=True)
|
want_dnssec=True)
|
||||||
response = dns.query.udp(query, ns, 1)
|
response = dns.query.udp(query, ns, 1)
|
||||||
|
|
||||||
if response.rcode() != 0:
|
if response.rcode() != 0:
|
||||||
return 0
|
return 0
|
||||||
# HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
|
# HANDLE QUERY FAILED (SERVER ERROR OR NO DNSKEY RECORD)
|
||||||
|
|
||||||
# answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
|
# answer should contain two RRSET: DNSKEY and RRSIG(DNSKEY)
|
||||||
answer = response.answer
|
answer = response.answer
|
||||||
if len(answer) != 2:
|
if len(answer) != 2:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
# the DNSKEY should be self signed, validate it
|
# the DNSKEY should be self signed, validate it
|
||||||
name = dns.name.from_text(sub)
|
name = dns.name.from_text(sub)
|
||||||
try:
|
try:
|
||||||
dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
|
dns.dnssec.validate(answer[0], answer[1], {name: answer[0]})
|
||||||
except dns.dnssec.ValidationFailure:
|
except dns.dnssec.ValidationFailure:
|
||||||
return 0
|
return 0
|
||||||
except Exception,e:
|
except Exception, e:
|
||||||
return 0
|
return 0
|
||||||
return 1
|
return 1
|
||||||
|
|
Loading…
Reference in New Issue