forked from spesmilo/electrum
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathaudio_modem.py
148 lines (117 loc) · 4.57 KB
/
audio_modem.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
from electrum.plugins import BasePlugin, hook
from electrum_gui.qt.util import WaitingDialog, EnterButton
from electrum.util import print_msg, print_error
from electrum.i18n import _
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import traceback
import zlib
import json
from io import BytesIO
import sys
import platform
try:
import amodem.audio
import amodem.main
import amodem.config
print_error('Audio MODEM is available.')
amodem.log.addHandler(amodem.logging.StreamHandler(sys.stderr))
amodem.log.setLevel(amodem.logging.INFO)
except ImportError:
amodem = None
print_error('Audio MODEM is not found.')
class Plugin(BasePlugin):
def __init__(self, config, name):
BasePlugin.__init__(self, config, name)
if self.is_available():
self.modem_config = amodem.config.slowest()
self.library_name = {
'Linux': 'libportaudio.so'
}[platform.system()]
def fullname(self):
return 'Audio MODEM'
def description(self):
return ('Provides support for air-gapped transaction signing.\n\n'
'Requires http://github.com/romanz/amodem/')
def is_available(self):
return amodem is not None
def requires_settings(self):
return True
def settings_widget(self, window):
return EnterButton(_('Settings'), self.settings_dialog)
def settings_dialog(self):
d = QDialog()
d.setWindowTitle("Settings")
layout = QGridLayout(d)
layout.addWidget(QLabel(_('Bit rate [kbps]: ')), 0, 0)
bitrates = list(sorted(amodem.config.bitrates.keys()))
def _index_changed(index):
bitrate = bitrates[index]
self.modem_config = amodem.config.bitrates[bitrate]
combo = QComboBox()
combo.addItems(map(str, bitrates))
combo.currentIndexChanged.connect(_index_changed)
layout.addWidget(combo, 0, 1)
ok_button = QPushButton(_("OK"))
ok_button.clicked.connect(d.accept)
layout.addWidget(ok_button, 1, 1)
return bool(d.exec_())
@hook
def transaction_dialog(self, dialog):
b = QPushButton()
b.setIcon(QIcon(":icons/speaker.png"))
def handler():
blob = json.dumps(dialog.tx.as_dict())
self.sender = self._send(parent=dialog, blob=blob)
self.sender.start()
b.clicked.connect(handler)
dialog.buttons.insert(0, b)
@hook
def scan_text_edit(self, parent):
def handler():
self.receiver = self._recv(parent=parent)
self.receiver.start()
parent.addButton(':icons/microphone.png', handler, _("Read from microphone"))
@hook
def show_text_edit(self, parent):
def handler():
blob = str(parent.toPlainText())
self.sender = self._send(parent=parent, blob=blob)
self.sender.start()
parent.addButton(':icons/speaker.png', handler, _("Send to speaker"))
def _audio_interface(self):
interface = amodem.audio.Interface(config=self.modem_config)
return interface.load(self.library_name)
def _send(self, parent, blob):
def sender_thread():
try:
with self._audio_interface() as interface:
src = BytesIO(blob)
dst = interface.player()
amodem.main.send(config=self.modem_config, src=src, dst=dst)
except Exception:
traceback.print_exc()
print_msg('Sending:', repr(blob))
blob = zlib.compress(blob)
kbps = self.modem_config.modem_bps / 1e3
msg = 'Sending to Audio MODEM ({0:.1f} kbps)...'.format(kbps)
return WaitingDialog(parent=parent, message=msg, run_task=sender_thread)
def _recv(self, parent):
def receiver_thread():
try:
with self._audio_interface() as interface:
src = interface.recorder()
dst = BytesIO()
amodem.main.recv(config=self.modem_config, src=src, dst=dst)
return dst.getvalue()
except Exception:
traceback.print_exc()
def on_success(blob):
if blob:
blob = zlib.decompress(blob)
print_msg('Received:', repr(blob))
parent.setText(blob)
kbps = self.modem_config.modem_bps / 1e3
msg = 'Receiving from Audio MODEM ({0:.1f} kbps)...'.format(kbps)
return WaitingDialog(parent=parent, message=msg,
run_task=receiver_thread, on_success=on_success)