3 from decimal import Decimal
5 from PyQt4.QtGui import *
6 from PyQt4.QtCore import *
7 import PyQt4.QtCore as QtCore
8 import PyQt4.QtGui as QtGui
10 from electrum_gui.qrcodewidget import QRCodeWidget
11 from electrum_gui import bmp, pyqrnative
12 from electrum_gui.i18n import _
14 from electrum import util
16 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
20 from electrum_gui import BasePlugin
21 class Plugin(BasePlugin):
23 def fullname(self): return 'Aliases'
25 def description(self): return _('Retrieve aliases using http.')
28 self.aliases = self.config.get('aliases', {}) # aliases for addresses
29 self.authorities = self.config.get('authorities', {}) # trusted addresses
30 self.receipts = self.config.get('receipts',{}) # signed URIs
34 def timer_actions(self):
35 if self.gui.payto_e.hasFocus():
37 r = unicode( self.gui.payto_e.text() )
38 if r != self.gui.previous_payto_e:
39 self.gui.previous_payto_e = r
41 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
43 to_address = self.get_alias(r, True, self.gui.show_message, self.gui.question)
47 s = r + ' <' + to_address + '>'
48 self.gui.payto_e.setText(s)
51 def get_alias(self, alias, interactive = False, show_message=None, question = None):
53 target, signing_address, auth_name = read_alias(self, alias)
54 except BaseException, e:
55 # raise exception if verify fails (verify the chain)
57 show_message("Alias error: " + str(e))
60 print target, signing_address, auth_name
63 a = self.aliases.get(alias)
65 msg = "Warning: the alias '%s' is self-signed.\nThe signing address is %s.\n\nDo you want to add this alias to your list of contacts?"%(alias,signing_address)
66 if interactive and question( msg ):
67 self.aliases[alias] = (signing_address, target)
71 if signing_address != a[0]:
72 msg = "Warning: the key of alias '%s' has changed since your last visit! It is possible that someone is trying to do something nasty!!!\nDo you accept to change your trusted key?"%alias
73 if interactive and question( msg ):
74 self.aliases[alias] = (signing_address, target)
78 if signing_address not in self.authorities.keys():
79 msg = "The alias: '%s' links to %s\n\nWarning: this alias was signed by an unknown key.\nSigning authority: %s\nSigning address: %s\n\nDo you want to add this key to your list of trusted keys?"%(alias,target,auth_name,signing_address)
80 if interactive and question( msg ):
81 self.authorities[signing_address] = auth_name
86 self.aliases[alias] = (signing_address, target)
92 def read_alias(self, alias):
95 m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
96 m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
98 url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1)
100 url = 'https://' + alias + '/bitcoin.id'
104 lines = urllib.urlopen(url).readlines()
109 line = lines[0].strip().split(':')
112 target = signing_addr = line[0]
114 target, auth_name, signing_addr, signature = line
115 msg = "alias:%s:%s:%s"%(alias,target,auth_name)
117 EC_KEY.verify_message(signing_addr, signature, msg)
119 # other lines are signed updates
120 for line in lines[1:]:
122 if not line: continue
123 line = line.split(':')
126 target, signature = line
127 EC_KEY.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
129 if not is_valid(target):
130 raise ValueError("Invalid bitcoin address")
132 return target, signing_addr, auth_name
135 def set_url(self, url, show_message, question):
136 payto, amount, label, message, signature, identity, url = util.parse_url(url)
138 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
139 signing_address = get_alias(identity, True, show_message, question)
140 elif is_valid(identity):
141 signing_address = identity
143 signing_address = None
144 if not signing_address:
147 EC_KEY.verify_message(signing_address, signature, url )
148 self.receipt = (signing_address, signature, url)
150 show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
151 address = amount = label = identity = message = ''
153 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', payto):
154 payto_address = get_alias(payto, True, show_message, question)
156 payto += ' <' + payto_address + '>'
158 return payto, amount, label, message, signature, identity, url
162 def update_contacts_tab(self, l):
164 for alias, v in self.aliases.items():
166 alias_targets.append(target)
167 item = QTreeWidgetItem( [ target, alias, '-'] )
168 item.setBackgroundColor(0, QColor('lightgray'))
169 item.setData(0,32,False)
170 item.setData(0,33,alias + ' <' + target + '>')
171 l.insertTopLevelItem(0,item)
174 def update_completions(self, l):
175 l[:] = l + self.aliases.keys()
178 def create_contact_menu(self, menu, item):
179 label = unicode(item.text(1))
180 if label in self.aliases.keys():
181 addr = unicode(item.text(0))
182 label = unicode(item.text(1))
183 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
184 menu.addAction(_("Delete alias"), lambda: delete_alias(self, label))
187 def show_contact_details(self, m):
188 a = self.aliases.get(m)
190 if a[0] in self.authorities.keys():
191 s = self.authorities.get(a[0])
194 msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0]
195 QMessageBox.information(self.gui, 'Alias', msg, 'OK')
198 def delete_alias(self, x):
199 if self.gui.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
200 if x in self.aliases:
202 self.update_history_tab()
203 self.update_contacts_tab()
204 self.update_completions()