2 changed files with 222 additions and 0 deletions
@ -0,0 +1,216 @@ |
|||||||
|
#!/usr/bin/env python |
||||||
|
# |
||||||
|
# Electrum - Lightweight Bitcoin Client |
||||||
|
# Copyright (C) 2015 Thomas Voegtlin |
||||||
|
# |
||||||
|
# This program is free software: you can redistribute it and/or modify |
||||||
|
# it under the terms of the GNU General Public License as published by |
||||||
|
# the Free Software Foundation, either version 3 of the License, or |
||||||
|
# (at your option) any later version. |
||||||
|
# |
||||||
|
# This program is distributed in the hope that it will be useful, |
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
||||||
|
# GNU General Public License for more details. |
||||||
|
# |
||||||
|
# You should have received a copy of the GNU General Public License |
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
||||||
|
|
||||||
|
from __future__ import absolute_import |
||||||
|
|
||||||
|
import socket |
||||||
|
import time |
||||||
|
import threading |
||||||
|
import base64 |
||||||
|
import re |
||||||
|
import platform |
||||||
|
from decimal import Decimal |
||||||
|
from Queue import Queue |
||||||
|
|
||||||
|
import smtplib |
||||||
|
import imaplib |
||||||
|
import email |
||||||
|
from email.MIMEMultipart import MIMEMultipart |
||||||
|
from email.MIMEBase import MIMEBase |
||||||
|
from email import Encoders |
||||||
|
|
||||||
|
from PyQt4.QtGui import * |
||||||
|
from PyQt4.QtCore import * |
||||||
|
import PyQt4.QtCore as QtCore |
||||||
|
import PyQt4.QtGui as QtGui |
||||||
|
|
||||||
|
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_EXPIRED |
||||||
|
from electrum.plugins import BasePlugin, hook |
||||||
|
from electrum import util |
||||||
|
from electrum.paymentrequest import PaymentRequest |
||||||
|
from electrum.i18n import _ |
||||||
|
from electrum_gui.qt.util import text_dialog, EnterButton |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Processor(threading.Thread): |
||||||
|
polling_interval = 5*60 |
||||||
|
|
||||||
|
def __init__(self, imap_server, username, password, callback): |
||||||
|
threading.Thread.__init__(self) |
||||||
|
self.daemon = True |
||||||
|
self.username = username |
||||||
|
self.password = password |
||||||
|
self.imap_server = imap_server |
||||||
|
self.on_receive = callback |
||||||
|
|
||||||
|
def poll(self): |
||||||
|
try: |
||||||
|
self.M.select() |
||||||
|
except: |
||||||
|
return |
||||||
|
typ, data = self.M.search(None, 'ALL') |
||||||
|
for num in data[0].split(): |
||||||
|
typ, msg_data = self.M.fetch(num, '(RFC822)') |
||||||
|
msg = email.message_from_string(msg_data[0][1]) |
||||||
|
p = msg.get_payload() |
||||||
|
if not msg.is_multipart(): |
||||||
|
p = [p] |
||||||
|
continue |
||||||
|
for item in p: |
||||||
|
if item.get_content_type() == "application/bitcoin-paymentrequest": |
||||||
|
pr_str = item.get_payload() |
||||||
|
pr_str = base64.b64decode(pr_str) |
||||||
|
self.on_receive(pr_str) |
||||||
|
|
||||||
|
def run(self): |
||||||
|
self.M = imaplib.IMAP4_SSL(self.imap_server) |
||||||
|
self.M.login(self.username, self.password) |
||||||
|
while True: |
||||||
|
self.poll() |
||||||
|
time.sleep(self.polling_interval) |
||||||
|
self.M.close() |
||||||
|
self.M.logout() |
||||||
|
|
||||||
|
def send(self, recipient, message, payment_request): |
||||||
|
msg = MIMEMultipart() |
||||||
|
msg['Subject'] = message |
||||||
|
msg['To'] = recipient |
||||||
|
msg['From'] = self.username |
||||||
|
part = MIMEBase('application', "bitcoin-paymentrequest") |
||||||
|
part.set_payload(payment_request) |
||||||
|
Encoders.encode_base64(part) |
||||||
|
part.add_header('Content-Disposition', 'attachment; filename="payreq.btc"') |
||||||
|
msg.attach(part) |
||||||
|
s = smtplib.SMTP_SSL(self.imap_server, timeout=2) |
||||||
|
s.login(self.username, self.password) |
||||||
|
s.sendmail(self.username, [recipient], msg.as_string()) |
||||||
|
s.quit() |
||||||
|
|
||||||
|
|
||||||
|
class Plugin(BasePlugin): |
||||||
|
|
||||||
|
def fullname(self): |
||||||
|
return 'Email' |
||||||
|
|
||||||
|
def description(self): |
||||||
|
return _("Send and receive payment requests via email") |
||||||
|
|
||||||
|
def is_available(self): |
||||||
|
return True |
||||||
|
|
||||||
|
def __init__(self, a, b): |
||||||
|
BasePlugin.__init__(self, a, b) |
||||||
|
self.imap_server = self.config.get('email_server', '') |
||||||
|
self.username = self.config.get('email_username', '') |
||||||
|
self.password = self.config.get('email_password', '') |
||||||
|
if self.imap_server and self.username and self.password: |
||||||
|
self.processor = Processor(self.imap_server, self.username, self.password, self.on_receive) |
||||||
|
self.processor.start() |
||||||
|
self.win = None |
||||||
|
|
||||||
|
def on_receive(self, pr_str): |
||||||
|
self.print_error('received payment request') |
||||||
|
self.pr = PaymentRequest(pr_str) |
||||||
|
if self.win: |
||||||
|
self.win.emit(SIGNAL('email:new_invoice')) |
||||||
|
|
||||||
|
def new_invoice(self): |
||||||
|
self.win.invoices.add(self.pr) |
||||||
|
self.win.update_invoices_list() |
||||||
|
|
||||||
|
@hook |
||||||
|
def init_qt(self, gui): |
||||||
|
from electrum_gui.qt.util import ThreadedButton |
||||||
|
self.win = gui.main_window |
||||||
|
self.win.connect(self.win, SIGNAL('email:new_invoice'), self.new_invoice) |
||||||
|
|
||||||
|
self.send_button = EnterButton(_('Send'), self.send) |
||||||
|
self.win.receive_buttons.insertWidget(1, self.send_button) |
||||||
|
|
||||||
|
def send(self): |
||||||
|
addr = str(self.win.receive_address_e.text()) |
||||||
|
message = unicode(self.win.receive_message_e.text()) |
||||||
|
payment_request = self.win.make_payment_request(addr) |
||||||
|
|
||||||
|
if not self.win.save_payment_request(): |
||||||
|
return |
||||||
|
|
||||||
|
recipient, ok = QtGui.QInputDialog.getText(self.win, 'Send request', 'Send request to:') |
||||||
|
if not ok: |
||||||
|
return |
||||||
|
|
||||||
|
recipient = str(recipient) |
||||||
|
self.print_error('sending mail to', recipient) |
||||||
|
try: |
||||||
|
self.processor.send(recipient, message, payment_request) |
||||||
|
except BaseException as e: |
||||||
|
self.win.show_message(str(e)) |
||||||
|
return |
||||||
|
|
||||||
|
self.win.show_message(_('Request sent.')) |
||||||
|
|
||||||
|
|
||||||
|
def requires_settings(self): |
||||||
|
return True |
||||||
|
|
||||||
|
def settings_widget(self, window): |
||||||
|
return EnterButton(_('Settings'), self.settings_dialog) |
||||||
|
|
||||||
|
def settings_dialog(self, x): |
||||||
|
from electrum_gui.qt.util import Buttons, CloseButton, OkButton |
||||||
|
|
||||||
|
d = QDialog(self.window) |
||||||
|
d.setWindowTitle("Email settings") |
||||||
|
d.setMinimumSize(500, 200) |
||||||
|
|
||||||
|
vbox = QVBoxLayout(d) |
||||||
|
grid = QGridLayout() |
||||||
|
vbox.addLayout(grid) |
||||||
|
|
||||||
|
grid.addWidget(QLabel('Server'), 0, 0) |
||||||
|
server_e = QLineEdit() |
||||||
|
server_e.setText(self.imap_server) |
||||||
|
grid.addWidget(server_e, 0, 1) |
||||||
|
|
||||||
|
grid.addWidget(QLabel('Username'), 1, 0) |
||||||
|
username_e = QLineEdit() |
||||||
|
username_e.setText(self.username) |
||||||
|
grid.addWidget(username_e, 1, 1) |
||||||
|
|
||||||
|
grid.addWidget(QLabel('Password'), 2, 0) |
||||||
|
password_e = QLineEdit() |
||||||
|
password_e.setText(self.password) |
||||||
|
grid.addWidget(password_e, 2, 1) |
||||||
|
|
||||||
|
vbox.addStretch() |
||||||
|
vbox.addLayout(Buttons(CloseButton(d), OkButton(d))) |
||||||
|
|
||||||
|
if not d.exec_(): |
||||||
|
return |
||||||
|
|
||||||
|
server = str(server_e.text()) |
||||||
|
self.config.set_key('email_server', server) |
||||||
|
|
||||||
|
username = str(username_e.text()) |
||||||
|
self.config.set_key('email_username', username) |
||||||
|
|
||||||
|
password = str(password_e.text()) |
||||||
|
self.config.set_key('email_password', password) |
||||||
|
|
||||||
|
|
||||||
Loading…
Reference in new issue