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 import bmp, pyqrnative
11 from electrum.i18n import _
13 from electrum import util
15 ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$'
19 from electrum.plugins 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
32 def is_available(self):
35 def timer_actions(self):
36 if self.gui.payto_e.hasFocus():
38 r = unicode( self.gui.payto_e.text() )
39 if r != self.gui.previous_payto_e:
40 self.gui.previous_payto_e = r
42 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r):
44 to_address = self.get_alias(r, True, self.gui.show_message, self.gui.question)
48 s = r + ' <' + to_address + '>'
49 self.gui.payto_e.setText(s)
52 def get_alias(self, alias, interactive = False, show_message=None, question = None):
54 target, signing_address, auth_name = read_alias(self, alias)
55 except Exception as e:
56 # raise exception if verify fails (verify the chain)
58 show_message("Alias error: " + str(e))
61 print target, signing_address, auth_name
64 a = self.aliases.get(alias)
66 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)
67 if interactive and question( msg ):
68 self.aliases[alias] = (signing_address, target)
72 if signing_address != a[0]:
73 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
74 if interactive and question( msg ):
75 self.aliases[alias] = (signing_address, target)
79 if signing_address not in self.authorities.keys():
80 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)
81 if interactive and question( msg ):
82 self.authorities[signing_address] = auth_name
87 self.aliases[alias] = (signing_address, target)
93 def read_alias(self, alias):
96 m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias)
97 m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias)
99 url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1)
101 url = 'https://' + alias + '/bitcoin.id'
105 lines = urllib.urlopen(url).readlines()
110 line = lines[0].strip().split(':')
113 target = signing_addr = line[0]
115 target, auth_name, signing_addr, signature = line
116 msg = "alias:%s:%s:%s"%(alias,target,auth_name)
118 EC_KEY.verify_message(signing_addr, signature, msg)
120 # other lines are signed updates
121 for line in lines[1:]:
123 if not line: continue
124 line = line.split(':')
127 target, signature = line
128 EC_KEY.verify_message(previous, signature, "alias:%s:%s"%(alias,target))
130 if not is_valid(target):
131 raise ValueError("Invalid bitcoin address")
133 return target, signing_addr, auth_name
136 def set_url(self, url, show_message, question):
137 payto, amount, label, message, signature, identity, url = util.parse_url(url)
139 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity):
140 signing_address = get_alias(identity, True, show_message, question)
141 elif is_valid(identity):
142 signing_address = identity
144 signing_address = None
145 if not signing_address:
148 EC_KEY.verify_message(signing_address, signature, url )
149 self.receipt = (signing_address, signature, url)
151 show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.')
152 address = amount = label = identity = message = ''
154 if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', payto):
155 payto_address = get_alias(payto, True, show_message, question)
157 payto += ' <' + payto_address + '>'
159 return payto, amount, label, message, signature, identity, url
163 def update_contacts_tab(self, l):
165 for alias, v in self.aliases.items():
167 alias_targets.append(target)
168 item = QTreeWidgetItem( [ target, alias, '-'] )
169 item.setBackgroundColor(0, QColor('lightgray'))
170 item.setData(0,32,False)
171 item.setData(0,33,alias + ' <' + target + '>')
172 l.insertTopLevelItem(0,item)
175 def update_completions(self, l):
176 l[:] = l + self.aliases.keys()
179 def create_contact_menu(self, menu, item):
180 label = unicode(item.text(1))
181 if label in self.aliases.keys():
182 addr = unicode(item.text(0))
183 label = unicode(item.text(1))
184 menu.addAction(_("View alias details"), lambda: self.show_contact_details(label))
185 menu.addAction(_("Delete alias"), lambda: delete_alias(self, label))
188 def show_contact_details(self, m):
189 a = self.aliases.get(m)
191 if a[0] in self.authorities.keys():
192 s = self.authorities.get(a[0])
195 msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0]
196 QMessageBox.information(self.gui, 'Alias', msg, 'OK')
199 def delete_alias(self, x):
200 if self.gui.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")):
201 if x in self.aliases:
203 self.update_history_tab()
204 self.update_contacts_tab()
205 self.update_completions()