Maran
12 years ago
28 changed files with 868 additions and 685 deletions
@ -1 +1,2 @@ |
|||
# do not remove this file |
|||
from plugins import BasePlugin |
|||
|
@ -0,0 +1,32 @@ |
|||
|
|||
|
|||
class BasePlugin: |
|||
|
|||
def get_info(self): |
|||
return self.fullname, self.description |
|||
|
|||
def __init__(self, gui, name, fullname, description): |
|||
self.name = name |
|||
self.fullname = fullname |
|||
self.description = description |
|||
self.gui = gui |
|||
self.config = gui.config |
|||
|
|||
def toggle(self): |
|||
enabled = not self.is_enabled() |
|||
self.set_enabled(enabled) |
|||
self.init_gui() |
|||
return enabled |
|||
|
|||
def init_gui(self): |
|||
pass |
|||
|
|||
def is_enabled(self): |
|||
return self.is_available() and self.config.get('use_'+self.name) is True |
|||
|
|||
def is_available(self): |
|||
return True |
|||
|
|||
def set_enabled(self, enabled): |
|||
self.config.set_key('use_'+self.name, enabled, True) |
|||
|
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,199 @@ |
|||
import re |
|||
import platform |
|||
from decimal import Decimal |
|||
|
|||
from PyQt4.QtGui import * |
|||
from PyQt4.QtCore import * |
|||
import PyQt4.QtCore as QtCore |
|||
import PyQt4.QtGui as QtGui |
|||
|
|||
from electrum_gui.qrcodewidget import QRCodeWidget |
|||
from electrum_gui import bmp, pyqrnative |
|||
from electrum_gui.i18n import _ |
|||
|
|||
|
|||
ALIAS_REGEXP = '^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$' |
|||
|
|||
|
|||
|
|||
from electrum_gui import BasePlugin |
|||
class Plugin(BasePlugin): |
|||
|
|||
def __init__(self, gui): |
|||
BasePlugin.__init__(self, gui, 'aliases', 'Aliases', _('Retrieve aliases using http.')) |
|||
self.aliases = self.config.get('aliases', {}) # aliases for addresses |
|||
self.authorities = self.config.get('authorities', {}) # trusted addresses |
|||
self.receipts = self.config.get('receipts',{}) # signed URIs |
|||
|
|||
|
|||
def timer_actions(self): |
|||
if self.gui.payto_e.hasFocus(): |
|||
return |
|||
r = unicode( self.gui.payto_e.text() ) |
|||
if r != self.gui.previous_payto_e: |
|||
self.gui.previous_payto_e = r |
|||
r = r.strip() |
|||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', r): |
|||
try: |
|||
to_address = self.get_alias(r, True, self.gui.show_message, self.gui.question) |
|||
except: |
|||
return |
|||
if to_address: |
|||
s = r + ' <' + to_address + '>' |
|||
self.gui.payto_e.setText(s) |
|||
|
|||
|
|||
def get_alias(self, alias, interactive = False, show_message=None, question = None): |
|||
try: |
|||
target, signing_address, auth_name = read_alias(self, alias) |
|||
except BaseException, e: |
|||
# raise exception if verify fails (verify the chain) |
|||
if interactive: |
|||
show_message("Alias error: " + str(e)) |
|||
return |
|||
|
|||
print target, signing_address, auth_name |
|||
|
|||
if auth_name is None: |
|||
a = self.aliases.get(alias) |
|||
if not a: |
|||
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) |
|||
if interactive and question( msg ): |
|||
self.aliases[alias] = (signing_address, target) |
|||
else: |
|||
target = None |
|||
else: |
|||
if signing_address != a[0]: |
|||
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 |
|||
if interactive and question( msg ): |
|||
self.aliases[alias] = (signing_address, target) |
|||
else: |
|||
target = None |
|||
else: |
|||
if signing_address not in self.authorities.keys(): |
|||
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) |
|||
if interactive and question( msg ): |
|||
self.authorities[signing_address] = auth_name |
|||
else: |
|||
target = None |
|||
|
|||
if target: |
|||
self.aliases[alias] = (signing_address, target) |
|||
|
|||
return target |
|||
|
|||
|
|||
|
|||
def read_alias(self, alias): |
|||
import urllib |
|||
|
|||
m1 = re.match('([\w\-\.]+)@((\w[\w\-]+\.)+[\w\-]+)', alias) |
|||
m2 = re.match('((\w[\w\-]+\.)+[\w\-]+)', alias) |
|||
if m1: |
|||
url = 'https://' + m1.group(2) + '/bitcoin.id/' + m1.group(1) |
|||
elif m2: |
|||
url = 'https://' + alias + '/bitcoin.id' |
|||
else: |
|||
return '' |
|||
try: |
|||
lines = urllib.urlopen(url).readlines() |
|||
except: |
|||
return '' |
|||
|
|||
# line 0 |
|||
line = lines[0].strip().split(':') |
|||
if len(line) == 1: |
|||
auth_name = None |
|||
target = signing_addr = line[0] |
|||
else: |
|||
target, auth_name, signing_addr, signature = line |
|||
msg = "alias:%s:%s:%s"%(alias,target,auth_name) |
|||
print msg, signature |
|||
EC_KEY.verify_message(signing_addr, signature, msg) |
|||
|
|||
# other lines are signed updates |
|||
for line in lines[1:]: |
|||
line = line.strip() |
|||
if not line: continue |
|||
line = line.split(':') |
|||
previous = target |
|||
print repr(line) |
|||
target, signature = line |
|||
EC_KEY.verify_message(previous, signature, "alias:%s:%s"%(alias,target)) |
|||
|
|||
if not is_valid(target): |
|||
raise ValueError("Invalid bitcoin address") |
|||
|
|||
return target, signing_addr, auth_name |
|||
|
|||
|
|||
def set_url(self, url, show_message, question): |
|||
payto, amount, label, message, signature, identity, url = util.parse_url(url) |
|||
if signature: |
|||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', identity): |
|||
signing_address = get_alias(identity, True, show_message, question) |
|||
elif is_valid(identity): |
|||
signing_address = identity |
|||
else: |
|||
signing_address = None |
|||
if not signing_address: |
|||
return |
|||
try: |
|||
EC_KEY.verify_message(signing_address, signature, url ) |
|||
self.receipt = (signing_address, signature, url) |
|||
except: |
|||
show_message('Warning: the URI contains a bad signature.\nThe identity of the recipient cannot be verified.') |
|||
address = amount = label = identity = message = '' |
|||
|
|||
if re.match('^(|([\w\-\.]+)@)((\w[\w\-]+\.)+[\w\-]+)$', address): |
|||
payto_address = get_alias(address, True, show_message, question) |
|||
if payto_address: |
|||
address = address + ' <' + payto_address + '>' |
|||
|
|||
return address, amount, label, message, signature, identity, url |
|||
|
|||
|
|||
|
|||
def update_contacts_tab(self, l): |
|||
alias_targets = [] |
|||
for alias, v in self.aliases.items(): |
|||
s, target = v |
|||
alias_targets.append(target) |
|||
item = QTreeWidgetItem( [ target, alias, '-'] ) |
|||
item.setBackgroundColor(0, QColor('lightgray')) |
|||
item.setData(0,32,False) |
|||
item.setData(0,33,alias + ' <' + target + '>') |
|||
l.insertTopLevelItem(0,item) |
|||
|
|||
|
|||
def update_completions(self, l): |
|||
l[:] = l + self.aliases.keys() |
|||
|
|||
|
|||
def create_contact_menu(self, menu, item): |
|||
label = unicode(item.text(1)) |
|||
if label in self.aliases.keys(): |
|||
addr = unicode(item.text(0)) |
|||
label = unicode(item.text(1)) |
|||
menu.addAction(_("View alias details"), lambda: self.show_contact_details(label)) |
|||
menu.addAction(_("Delete alias"), lambda: delete_alias(self, label)) |
|||
|
|||
|
|||
def show_contact_details(self, m): |
|||
a = self.aliases.get(m) |
|||
if a: |
|||
if a[0] in self.authorities.keys(): |
|||
s = self.authorities.get(a[0]) |
|||
else: |
|||
s = "self-signed" |
|||
msg = _('Alias:')+' '+ m + '\n'+_('Target address:')+' '+ a[1] + '\n\n'+_('Signed by:')+' ' + s + '\n'+_('Signing address:')+' ' + a[0] |
|||
QMessageBox.information(self.gui, 'Alias', msg, 'OK') |
|||
|
|||
|
|||
def delete_alias(self, x): |
|||
if self.gui.question(_("Do you want to remove")+" %s "%x +_("from your list of contacts?")): |
|||
if x in self.aliases: |
|||
self.aliases.pop(x) |
|||
self.update_history_tab() |
|||
self.update_contacts_tab() |
|||
self.update_completions() |
@ -0,0 +1,65 @@ |
|||
from PyQt4.QtGui import * |
|||
from electrum_gui import BasePlugin |
|||
from electrum_gui.i18n import _ |
|||
|
|||
class Plugin(BasePlugin): |
|||
|
|||
|
|||
def __init__(self, gui): |
|||
BasePlugin.__init__(self, gui, 'virtualkeyboard', 'Virtual Keyboard', |
|||
_("Add an optional, mouse keyboard to the password dialog.\nWarning: do not use this if it makes you pick a weaker password.")) |
|||
self.vkb = None |
|||
self.vkb_index = 0 |
|||
|
|||
|
|||
def password_dialog(self, pw, grid, pos): |
|||
vkb_button = QPushButton(_("+")) |
|||
vkb_button.setFixedWidth(20) |
|||
vkb_button.clicked.connect(lambda: self.toggle_vkb(grid, pw)) |
|||
grid.addWidget(vkb_button, pos, 2) |
|||
self.kb_pos = 2 |
|||
|
|||
|
|||
def toggle_vkb(self, grid, pw): |
|||
if self.vkb: grid.removeItem(self.vkb) |
|||
self.vkb = self.virtual_keyboard(self.vkb_index, pw) |
|||
grid.addLayout(self.vkb, self.kb_pos, 0, 1, 3) |
|||
self.vkb_index += 1 |
|||
|
|||
|
|||
def virtual_keyboard(self, i, pw): |
|||
import random |
|||
i = i%3 |
|||
if i == 0: |
|||
chars = 'abcdefghijklmnopqrstuvwxyz ' |
|||
elif i == 1: |
|||
chars = 'ABCDEFGHIJKLMNOPQRTSUVWXYZ ' |
|||
elif i == 2: |
|||
chars = '1234567890!?.,;:/%&()[]{}+-' |
|||
|
|||
n = len(chars) |
|||
s = [] |
|||
for i in xrange(n): |
|||
while True: |
|||
k = random.randint(0,n-1) |
|||
if k not in s: |
|||
s.append(k) |
|||
break |
|||
|
|||
def add_target(t): |
|||
return lambda: pw.setText(str( pw.text() ) + t) |
|||
|
|||
vbox = QVBoxLayout() |
|||
grid = QGridLayout() |
|||
grid.setSpacing(2) |
|||
for i in range(n): |
|||
l_button = QPushButton(chars[s[i]]) |
|||
l_button.setFixedWidth(25) |
|||
l_button.setFixedHeight(25) |
|||
l_button.clicked.connect(add_target(chars[s[i]]) ) |
|||
grid.addWidget(l_button, i/6, i%6) |
|||
|
|||
vbox.addLayout(grid) |
|||
|
|||
return vbox |
|||
|
Loading…
Reference in new issue