From c67c4e95dca6e0d5d995d8a11fea666918bcaab9 Mon Sep 17 00:00:00 2001 From: ThomasV Date: Tue, 24 May 2022 11:48:14 +0200 Subject: [PATCH] remove email plugin --- electrum/plugins/email_requests/__init__.py | 5 - electrum/plugins/email_requests/qt.py | 282 -------------------- 2 files changed, 287 deletions(-) delete mode 100644 electrum/plugins/email_requests/__init__.py delete mode 100644 electrum/plugins/email_requests/qt.py diff --git a/electrum/plugins/email_requests/__init__.py b/electrum/plugins/email_requests/__init__.py deleted file mode 100644 index 8c9e82472..000000000 --- a/electrum/plugins/email_requests/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -from electrum.i18n import _ - -fullname = _('Email') -description = _("Send and receive payment request with an email account") -available_for = ['qt'] diff --git a/electrum/plugins/email_requests/qt.py b/electrum/plugins/email_requests/qt.py deleted file mode 100644 index da68214b3..000000000 --- a/electrum/plugins/email_requests/qt.py +++ /dev/null @@ -1,282 +0,0 @@ -#!/usr/bin/env python -# -# Electrum - Lightweight Bitcoin Client -# Copyright (C) 2015 Thomas Voegtlin -# -# Permission is hereby granted, free of charge, to any person -# obtaining a copy of this software and associated documentation files -# (the "Software"), to deal in the Software without restriction, -# including without limitation the rights to use, copy, modify, merge, -# publish, distribute, sublicense, and/or sell copies of the Software, -# and to permit persons to whom the Software is furnished to do so, -# subject to the following conditions: -# -# The above copyright notice and this permission notice shall be -# included in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS -# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -# SOFTWARE. -import random -import time -import threading -import base64 -from functools import partial -import traceback -import sys -from typing import Set -import ssl -import smtplib -import imaplib -import email -from email.mime.multipart import MIMEMultipart -from email.mime.base import MIMEBase -from email.encoders import encode_base64 - -from PyQt5.QtCore import QObject, pyqtSignal, QThread -from PyQt5.QtWidgets import (QVBoxLayout, QLabel, QGridLayout, QLineEdit, - QInputDialog) -import certifi - -from electrum.gui.qt.util import (EnterButton, Buttons, CloseButton, OkButton, - WindowModalDialog) -from electrum.gui.qt.main_window import ElectrumWindow - -from electrum.plugin import BasePlugin, hook -from electrum.paymentrequest import PaymentRequest -from electrum.i18n import _ -from electrum.logging import Logger -from electrum.wallet import Abstract_Wallet -from electrum.invoices import Invoice - - -ca_path = certifi.where() -ssl_context = ssl.create_default_context(purpose=ssl.Purpose.SERVER_AUTH, cafile=ca_path) - - -class Processor(threading.Thread, Logger): - polling_interval = 5*60 - - def __init__(self, imap_server, username, password, callback): - threading.Thread.__init__(self) - Logger.__init__(self) - self.daemon = True - self.username = username - self.password = password - self.imap_server = imap_server - self.on_receive = callback - self.M = None - self.reset_connect_wait() - - def reset_connect_wait(self): - self.connect_wait = 100 # ms, between failed connection attempts - - def poll(self): - try: - self.M.select() - except: - return - typ, data = self.M.search(None, 'ALL') - for num in str(data[0], 'utf8').split(): - typ, msg_data = self.M.fetch(num, '(RFC822)') - msg = email.message_from_bytes(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): - while True: - try: - self.M = imaplib.IMAP4_SSL(self.imap_server, ssl_context=ssl_context) - self.M.login(self.username, self.password) - except BaseException as e: - self.logger.info(f'connecting failed: {repr(e)}') - self.connect_wait *= 2 - else: - self.reset_connect_wait() - # Reconnect when host changes - while self.M and self.M.host == self.imap_server: - try: - self.poll() - except BaseException as e: - self.logger.info(f'polling failed: {repr(e)}') - break - time.sleep(self.polling_interval) - time.sleep(random.randint(0, self.connect_wait)) - - 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) - encode_base64(part) - part.add_header('Content-Disposition', 'attachment; filename="payreq.btc"') - msg.attach(part) - try: - s = smtplib.SMTP_SSL(self.imap_server, timeout=2, context=ssl_context) - s.login(self.username, self.password) - s.sendmail(self.username, [recipient], msg.as_string()) - s.quit() - except BaseException as e: - self.logger.info(e) - - -class QEmailSignalObject(QObject): - email_new_invoice_signal = pyqtSignal() - - -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, parent, config, name): - BasePlugin.__init__(self, parent, config, name) - 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.obj = QEmailSignalObject() - self.obj.email_new_invoice_signal.connect(self.new_invoice) - self.wallets = set() # type: Set[Abstract_Wallet] - - def on_receive(self, pr_str): - self.logger.info('received payment request') - self.pr = PaymentRequest(pr_str) - self.obj.email_new_invoice_signal.emit() - - @hook - def load_wallet(self, wallet, main_window): - self.wallets |= {wallet} - - @hook - def close_wallet(self, wallet): - self.wallets -= {wallet} - - def new_invoice(self): - invoice = Invoice.from_bip70_payreq(self.pr) - for wallet in self.wallets: - wallet.save_invoice(invoice) - #main_window.invoice_list.update() - - @hook - def receive_list_menu(self, window: ElectrumWindow, menu, addr): - menu.addAction(_("Send via e-mail"), lambda: self.send(window, addr)) - - def send(self, window: ElectrumWindow, addr): - from electrum import paymentrequest - req = window.wallet.get_request(addr) - # FIXME only on-chain requests are supported - message = req.message - if req.bip70: - payload = bytes.fromhex(req.bip70) - else: - pr = paymentrequest.make_request(self.config, req) - payload = pr.SerializeToString() - if not payload: - return - recipient, ok = QInputDialog.getText(window, 'Send request', 'Email invoice to:') - if not ok: - return - recipient = str(recipient) - self.logger.info(f'sending mail to {recipient}') - try: - # FIXME this runs in the GUI thread and blocks it... - self.processor.send(recipient, message, payload) - except BaseException as e: - self.logger.exception('') - window.show_message(repr(e)) - else: - window.show_message(_('Request sent.')) - - def requires_settings(self): - return True - - def settings_widget(self, window): - return EnterButton(_('Settings'), partial(self.settings_dialog, window)) - - def settings_dialog(self, window): - d = WindowModalDialog(window, _("Email settings")) - d.setMinimumSize(500, 200) - - vbox = QVBoxLayout(d) - vbox.addWidget(QLabel(_('Server hosting your email account'))) - grid = QGridLayout() - vbox.addLayout(grid) - grid.addWidget(QLabel('Server (IMAP)'), 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) - self.imap_server = server - - username = str(username_e.text()) - self.config.set_key('email_username', username) - self.username = username - - password = str(password_e.text()) - self.config.set_key('email_password', password) - self.password = password - - check_connection = CheckConnectionThread(server, username, password) - check_connection.connection_error_signal.connect(lambda e: window.show_message( - _("Unable to connect to mail server:\n {}").format(e) + "\n" + - _("Please check your connection and credentials.") - )) - check_connection.start() - - -class CheckConnectionThread(QThread): - connection_error_signal = pyqtSignal(str) - - def __init__(self, server, username, password): - super().__init__() - self.server = server - self.username = username - self.password = password - - def run(self): - try: - conn = imaplib.IMAP4_SSL(self.server, ssl_context=ssl_context) - conn.login(self.username, self.password) - except BaseException as e: - self.connection_error_signal.emit(repr(e))