From 2bc9c7ff4c83013cd2b7b5109dc7ff604759f2a1 Mon Sep 17 00:00:00 2001 From: mailchuck Date: Sat, 3 Oct 2015 17:24:21 +0200 Subject: [PATCH] Email gateway integration Sending/receiving from the send tab, reply from inbox and registration/unregistration context menu. --- src/bitmessageqt/__init__.py | 89 +++++++++++++++--- src/bitmessageqt/account.py | 52 +++++++++- src/bitmessageqt/emailgateway.py | 64 +++++++++++++ src/bitmessageqt/emailgateway.ui | 157 +++++++++++++++++++++++++++++++ 4 files changed, 347 insertions(+), 15 deletions(-) create mode 100644 src/bitmessageqt/emailgateway.py create mode 100644 src/bitmessageqt/emailgateway.ui diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 9507ea6b..06ddb900 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -34,6 +34,7 @@ from newsubscriptiondialog import * from regenerateaddresses import * from newchandialog import * from specialaddressbehavior import * +from emailgateway import * from settings import * from about import * from help import * @@ -213,6 +214,10 @@ class MyForm(QtGui.QMainWindow): _translate( "MainWindow", "Special address behavior..."), self.on_action_SpecialAddressBehaviorDialog) + self.actionEmailGateway = self.ui.addressContextMenuToolbarYourIdentities.addAction( + _translate( + "MainWindow", "Email gateway"), + self.on_action_EmailGatewayDialog) self.ui.treeWidgetYourIdentities.setContextMenuPolicy( QtCore.Qt.CustomContextMenu) @@ -230,6 +235,7 @@ class MyForm(QtGui.QMainWindow): self.popMenuYourIdentities.addAction(self.actionDisableYourIdentities) self.popMenuYourIdentities.addAction(self.actionSetAvatarYourIdentities) self.popMenuYourIdentities.addAction(self.actionSpecialAddressBehaviorYourIdentities) + self.popMenuYourIdentities.addAction(self.actionEmailGateway) def init_chan_popup_menu(self, connectSignal=True): # Popup menu for the Channels tab @@ -902,6 +908,7 @@ class MyForm(QtGui.QMainWindow): subjectItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) subjectItem.setToolTip(unicode(acct.subject, 'utf-8')) + subjectItem.setData(Qt.UserRole, str(subject)) subjectItem.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) tableWidget.setItem(0, 2, subjectItem) @@ -1038,6 +1045,7 @@ class MyForm(QtGui.QMainWindow): # subject subject_item = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8')) subject_item.setToolTip(unicode(acct.subject, 'utf-8')) + subject_item.setData(Qt.UserRole, str(subject)) subject_item.setFlags( QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled) if not read: @@ -1968,6 +1976,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f QMessageBox.about(self, _translate("MainWindow", "Message too long"), _translate( "MainWindow", "The message that you are trying to send is too long by %1 bytes. (The maximum is 261644 bytes). Please cut it down before sending.").arg(len(message) - (2 ** 18 - 500))) return + + acct = accountClass(fromAddress) if sendMessageToPeople: # To send a message to specific people (rather than broadcast) toAddressesList = [s.strip() @@ -1976,6 +1986,12 @@ more work your computer must do to send the message. A Time-To-Live of four or f toAddressesList)) # remove duplicate addresses. If the user has one address with a BM- and the same address without the BM-, this will not catch it. They'll send the message to the person twice. for toAddress in toAddressesList: if toAddress != '': + if toAddress.find("@") >= 0 and isinstance(acct, GatewayAccount): + acct.createMessage(toAddress, fromAddress, subject, message) + subject = acct.subject + toAddress = acct.toAddress + print "Subject: %s" % (subject) + print "address: %s" % (toAddress) status, addressVersionNumber, streamNumber, ripe = decodeAddress( toAddress) if status != 'success': @@ -2197,6 +2213,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) + newItem.setData(Qt.UserRole, str(subject)) + #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. self.ui.tableWidgetInbox.setItem(0, 2, newItem) # newItem = QtGui.QTableWidgetItem('Doing work necessary to send @@ -2242,6 +2260,8 @@ more work your computer must do to send the message. A Time-To-Live of four or f self.ui.tableWidgetInbox.setItem(0, 1, newItem) newItem = QtGui.QTableWidgetItem(unicode(acct.subject, 'utf-8)')) newItem.setToolTip(unicode(acct.subject, 'utf-8)')) + newItem.setData(Qt.UserRole, str(subject)) + #newItem.setData(Qt.UserRole, unicode(message, 'utf-8)')) # No longer hold the message in the table; we'll use a SQL query to display it as needed. newItem.setFont(font) self.ui.tableWidgetInbox.setItem(0, 2, newItem) @@ -2631,6 +2651,30 @@ more work your computer must do to send the message. A Time-To-Live of four or f shared.writeKeysFile() self.rerenderInboxToLabels() + def on_action_EmailGatewayDialog(self): + self.dialog = EmailGatewayDialog(self) + # For Modal dialogs + if self.dialog.exec_(): + addressAtCurrentRow = self.getCurrentAccount() + acct = accountClass(addressAtCurrentRow) + if isinstance(acct, GatewayAccount) and self.dialog.ui.radioButtonUnregister.isChecked(): + print "unregister" + acct.unregister() + shared.config.remove_option(addressAtCurrentRow, 'gateway') + shared.writeKeysFile() + elif (not isinstance(acct, GatewayAccount)) and self.dialog.ui.radioButtonRegister.isChecked(): + print "register" + email = str(self.dialog.ui.lineEditEmail.text().toUtf8()) + acct = MailchuckAccount(addressAtCurrentRow) + acct.register(email) + shared.config.set(addressAtCurrentRow, 'label', email) + shared.config.set(addressAtCurrentRow, 'gateway', 'mailchuck') + shared.writeKeysFile() + else: + print "well nothing" +# shared.writeKeysFile() +# self.rerenderInboxToLabels() + def click_NewAddressDialog(self): addresses = [] configSections = shared.config.sections() @@ -2638,11 +2682,11 @@ more work your computer must do to send the message. A Time-To-Live of four or f if addressInKeysFile == 'bitmessagesettings': continue addresses.append(addressInKeysFile) - self.dialog = Ui_NewAddressWizard(addresses) - self.dialog.exec_() +# self.dialog = Ui_NewAddressWizard(addresses) +# self.dialog.exec_() # print "Name: " + self.dialog.field("name").toString() # print "Email: " + self.dialog.field("email").toString() - return +# return self.dialog = NewAddressDialog(self) # For Modal dialogs if self.dialog.exec_(): @@ -2789,6 +2833,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f currentInboxRow = tableWidget.currentRow() toAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 0).data(Qt.UserRole).toPyObject()) + acct = accountClass(toAddressAtCurrentInboxRow) fromAddressAtCurrentInboxRow = str(tableWidget.item( currentInboxRow, 1).data(Qt.UserRole).toPyObject()) msgid = str(tableWidget.item( @@ -2798,6 +2843,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f if queryreturn != []: for row in queryreturn: messageAtCurrentInboxRow, = row + acct.parseMessage(toAddressAtCurrentInboxRow, fromAddressAtCurrentInboxRow, str(tableWidget.item(currentInboxRow, 2).data(Qt.UserRole).toPyObject()), messageAtCurrentInboxRow) if toAddressAtCurrentInboxRow == self.str_broadcast_subscribers: #TODO what does this if?.. a = a @@ -2810,7 +2856,7 @@ more work your computer must do to send the message. A Time-To-Live of four or f else: self.setBroadcastEnablementDependingOnWhetherThisIsAChanAddress(toAddressAtCurrentInboxRow) - self.ui.lineEditTo.setText(str(fromAddressAtCurrentInboxRow)) + self.ui.lineEditTo.setText(str(acct.fromLabel)) # If the previous message was to a chan then we should send our reply to the chan rather than to the particular person who sent the message. if shared.config.has_section(toAddressAtCurrentInboxRow): @@ -2827,12 +2873,10 @@ more work your computer must do to send the message. A Time-To-Live of four or f quotedText = self.quoted_text(unicode(messageAtCurrentInboxRow, 'utf-8')) self.ui.textEditMessage.setText(quotedText) - if tableWidget.item(currentInboxRow, 2).text()[0:3] in ['Re:', 'RE:']: - self.ui.lineEditSubject.setText( - tableWidget.item(currentInboxRow, 2).text()) + if acct.subject[0:3] in ['Re:', 'RE:']: + self.ui.lineEditSubject.setText(acct.subject) else: - self.ui.lineEditSubject.setText( - 'Re: ' + tableWidget.item(currentInboxRow, 2).text()) + self.ui.lineEditSubject.setText('Re: ' + acct.subject) self.ui.tabWidgetSend.setCurrentIndex(0) self.ui.tabWidget.setCurrentIndex(1) @@ -3774,6 +3818,23 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) +class EmailGatewayDialog(QtGui.QDialog): + + def __init__(self, parent): + QtGui.QWidget.__init__(self, parent) + self.ui = Ui_EmailGatewayDialog() + self.ui.setupUi(self) + self.parent = parent + addressAtCurrentRow = parent.getCurrentAccount() + acct = accountClass(addressAtCurrentRow) +# if isinstance(acct, GatewayAccount): + label = shared.config.get(addressAtCurrentRow, 'label') + if label.find("@mailchuck.com") > -1: + self.ui.lineEditEmail.setText(label) + + QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) + + class AddAddressDialog(QtGui.QDialog): def __init__(self, parent): @@ -4001,11 +4062,11 @@ def run(): if shared.safeConfigGetBoolean('bitmessagesettings', 'dontconnect'): myapp.showConnectDialog() # ask the user if we may connect - try: - if shared.config.get('bitmessagesettings', 'mailchuck') < 1: - myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck')) - except: - myapp.showMigrationWizard(0) +# try: +# if shared.config.get('bitmessagesettings', 'mailchuck') < 1: +# myapp.showMigrationWizard(shared.config.get('bitmessagesettings', 'mailchuck')) +# except: +# myapp.showMigrationWizard(0) # only show after wizards and connect dialogs have completed if not shared.config.getboolean('bitmessagesettings', 'startintray'): diff --git a/src/bitmessageqt/account.py b/src/bitmessageqt/account.py index 0ff0fa23..05613e47 100644 --- a/src/bitmessageqt/account.py +++ b/src/bitmessageqt/account.py @@ -5,6 +5,9 @@ import re import sys import inspect from helper_sql import * +from addresses import decodeAddress +from pyelliptic.openssl import OpenSSL +import time def accountClass(address): if not shared.config.has_section(address): @@ -57,6 +60,31 @@ class GatewayAccount(BMAccount): gatewayName = None def __init__(self, address): super(BMAccount, self).__init__(address) + + def send(self): + status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.toAddress) + ackdata = OpenSSL.rand(32) + t = () + sqlExecute( + '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', + '', + self.toAddress, + ripe, + self.fromAddress, + self.subject, + self.message, + ackdata, + int(time.time()), # sentTime (this will never change) + int(time.time()), # lastActionTime + 0, # sleepTill time. This will get set when the POW gets done. + 'msgqueued', + 0, # retryNumber + 'sent', # folder + 2, # encodingtype + shared.config.getint('bitmessagesettings', 'ttl') + ) + + shared.workerQueue.put(('sendmessage', self.toAddress)) def parseMessage(self, toAddress, fromAddress, subject, message): super(BMAccount, self).parseMessage(toAddress, fromAddress, subject, message) @@ -71,6 +99,26 @@ class MailchuckAccount(GatewayAccount): regExpOutgoing = re.compile("(\S+) (.*)") def __init__(self, address): super(GatewayAccount, self).__init__(address) + + def createMessage(self, toAddress, fromAddress, subject, message): + self.subject = toAddress + " " + subject + self.toAddress = self.relayAddress + self.fromAddress = fromAddress + self.message = message + + def register(self, email): + self.toAddress = self.registrationAddress + self.subject = email + self.message = "" + self.fromAddress = self.address + self.send() + + def unregister(self): + self.toAddress = self.unregistrationAddress + self.subject = "" + self.message = "" + self.fromAddress = self.address + self.send() def parseMessage(self, toAddress, fromAddress, subject, message): super(GatewayAccount, self).parseMessage(toAddress, fromAddress, subject, message) @@ -84,10 +132,12 @@ class MailchuckAccount(GatewayAccount): self.subject += matches.group(3) if not matches.group(2) is None: self.fromLabel = matches.group(2) + self.fromAddress = matches.group(2) if toAddress == self.relayAddress: matches = self.regExpOutgoing.search(subject) if not matches is None: if not matches.group(2) is None: self.subject = matches.group(2) if not matches.group(1) is None: - self.toLabel = matches.group(1) \ No newline at end of file + self.toLabel = matches.group(1) + self.toAddress = matches.group(1) \ No newline at end of file diff --git a/src/bitmessageqt/emailgateway.py b/src/bitmessageqt/emailgateway.py new file mode 100644 index 00000000..0df4dd01 --- /dev/null +++ b/src/bitmessageqt/emailgateway.py @@ -0,0 +1,64 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'emailgateway.ui' +# +# Created: Fri Apr 26 17:43:31 2013 +# by: PyQt4 UI code generator 4.9.4 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + _fromUtf8 = lambda s: s + +class Ui_EmailGatewayDialog(object): + def setupUi(self, EmailGatewayDialog): + EmailGatewayDialog.setObjectName(_fromUtf8("EmailGatewayDialog")) + EmailGatewayDialog.resize(386, 172) + self.gridLayout = QtGui.QGridLayout(EmailGatewayDialog) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.radioButtonRegister = QtGui.QRadioButton(EmailGatewayDialog) + self.radioButtonRegister.setChecked(True) + self.radioButtonRegister.setObjectName(_fromUtf8("radioButtonRegister")) + self.gridLayout.addWidget(self.radioButtonRegister, 1, 0, 1, 1) + self.radioButtonUnregister = QtGui.QRadioButton(EmailGatewayDialog) + self.radioButtonUnregister.setObjectName(_fromUtf8("radioButtonUnregister")) + self.gridLayout.addWidget(self.radioButtonUnregister, 4, 0, 1, 1) + self.label = QtGui.QLabel(EmailGatewayDialog) + self.label.setWordWrap(True) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout.addWidget(self.label, 0, 0, 1, 1) + self.label_2 = QtGui.QLabel(EmailGatewayDialog) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.gridLayout.addWidget(self.label_2, 2, 0, 1, 1) + self.lineEditEmail = QtGui.QLineEdit(EmailGatewayDialog) + self.lineEditEmail.setEnabled(True) + self.lineEditEmail.setObjectName(_fromUtf8("lineEditEmail")) + self.gridLayout.addWidget(self.lineEditEmail, 3, 0, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(EmailGatewayDialog) + self.buttonBox.setMinimumSize(QtCore.QSize(368, 0)) + self.buttonBox.setOrientation(QtCore.Qt.Horizontal) + self.buttonBox.setStandardButtons(QtGui.QDialogButtonBox.Cancel|QtGui.QDialogButtonBox.Ok) + self.buttonBox.setObjectName(_fromUtf8("buttonBox")) + self.gridLayout.addWidget(self.buttonBox, 5, 0, 1, 1) + + self.retranslateUi(EmailGatewayDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), EmailGatewayDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), EmailGatewayDialog.reject) + QtCore.QObject.connect(self.radioButtonRegister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setEnabled) + QtCore.QObject.connect(self.radioButtonUnregister, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditEmail.setDisabled) + QtCore.QMetaObject.connectSlotsByName(EmailGatewayDialog) + EmailGatewayDialog.setTabOrder(self.radioButtonRegister, self.lineEditEmail) + EmailGatewayDialog.setTabOrder(self.lineEditEmail, self.radioButtonUnregister) + EmailGatewayDialog.setTabOrder(self.radioButtonUnregister, self.buttonBox) + + def retranslateUi(self, EmailGatewayDialog): + EmailGatewayDialog.setWindowTitle(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButtonRegister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Register on email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.radioButtonUnregister.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Unregister from email gateway", None, QtGui.QApplication.UnicodeUTF8)) + self.label.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Email gateway allows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available.", None, QtGui.QApplication.UnicodeUTF8)) + self.label_2.setText(QtGui.QApplication.translate("EmailGatewayDialog", "Desired email address (including @mailchuck.com):", None, QtGui.QApplication.UnicodeUTF8)) + diff --git a/src/bitmessageqt/emailgateway.ui b/src/bitmessageqt/emailgateway.ui new file mode 100644 index 00000000..927df46a --- /dev/null +++ b/src/bitmessageqt/emailgateway.ui @@ -0,0 +1,157 @@ + + + EmailGatewayDialog + + + + 0 + 0 + 386 + 172 + + + + Email gateway + + + + + + true + + + Desired email address (including @mailchuck.com) + + + + + + + Register on email gateway + + + true + + + + + + + + 368 + 0 + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + + + + true + + + @mailchuck.com + + + + + + + Email gateway alows you to communicate with email users. Currently, only the Mailchuck email gateway (@mailchuck.com) is available. + + + true + + + + + + + Unregister from email gateway + + + false + + + + + + + radioButtonRegister + lineEditEmailAddress + buttonBox + + + + + buttonBox + accepted() + EmailGatewayDialog + accept() + + + 227 + 152 + + + 157 + 171 + + + + + buttonBox + rejected() + EmailGatewayDialog + reject() + + + 295 + 158 + + + 286 + 171 + + + + + radioButtonRegister + clicked(bool) + lineEditEmailAddress + setEnabled(bool) + + + 95 + 40 + + + 94 + 123 + + + + + radioButtonUnregister + clicked(bool) + lineEditEmailAddress + setDisabled(bool) + + + 139 + 19 + + + 187 + 123 + + + + +