From cb7618f1b6d7d9e59dda6c63ce1631ce0cf9380e Mon Sep 17 00:00:00 2001 From: Chuck Date: Wed, 10 Jul 2013 15:26:10 +0700 Subject: [PATCH] Adding special mailing list behavior for email formatted messages --- src/bitmessageqt/__init__.py | 4 + src/bitmessageqt/specialaddressbehavior.py | 141 +++++++++++---------- src/bitmessageqt/specialaddressbehavior.ui | 17 ++- src/class_pop3Server.py | 108 +++++++++++++++- src/class_receiveDataThread.py | 27 ++-- 5 files changed, 212 insertions(+), 85 deletions(-) diff --git a/src/bitmessageqt/__init__.py b/src/bitmessageqt/__init__.py index 78966a84..68234f08 100644 --- a/src/bitmessageqt/__init__.py +++ b/src/bitmessageqt/__init__.py @@ -1962,6 +1962,7 @@ class MyForm(QtGui.QMainWindow): shared.config.set(str(addressAtCurrentRow), 'mailinglistname', str( self.dialog.ui.lineEditMailingListName.text().toUtf8())) self.ui.tableWidgetYourIdentities.item(currentRow, 1).setTextColor(QtGui.QColor(137, 04, 177)) + shared.config.set(str(addressAtCurrentRow), 'foremail', str(self.dialog.ui.checkBoxWrapMessagesWithEmailHeaders.isChecked())) with open(shared.appdata + 'keys.dat', 'wb') as configfile: shared.config.write(configfile) self.rerenderInboxToLabels() @@ -2997,6 +2998,9 @@ class SpecialAddressBehaviorDialog(QtGui.QDialog): addressAtCurrentRow, 'mailinglistname') except: mailingListName = '' + + self.ui.checkBoxWrapMessagesWithEmailHeaders.setChecked( + shared.safeConfigGetBoolean(addressAtCurrentRow, 'foremail')) self.ui.lineEditMailingListName.setText( unicode(mailingListName, 'utf-8')) QtGui.QWidget.resize(self, QtGui.QWidget.sizeHint(self)) diff --git a/src/bitmessageqt/specialaddressbehavior.py b/src/bitmessageqt/specialaddressbehavior.py index 78ff890d..75360076 100644 --- a/src/bitmessageqt/specialaddressbehavior.py +++ b/src/bitmessageqt/specialaddressbehavior.py @@ -1,64 +1,77 @@ -# -*- coding: utf-8 -*- - -# Form implementation generated from reading ui file 'specialaddressbehavior.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_SpecialAddressBehaviorDialog(object): - def setupUi(self, SpecialAddressBehaviorDialog): - SpecialAddressBehaviorDialog.setObjectName(_fromUtf8("SpecialAddressBehaviorDialog")) - SpecialAddressBehaviorDialog.resize(386, 172) - self.gridLayout = QtGui.QGridLayout(SpecialAddressBehaviorDialog) - self.gridLayout.setObjectName(_fromUtf8("gridLayout")) - self.radioButtonBehaveNormalAddress = QtGui.QRadioButton(SpecialAddressBehaviorDialog) - self.radioButtonBehaveNormalAddress.setChecked(True) - self.radioButtonBehaveNormalAddress.setObjectName(_fromUtf8("radioButtonBehaveNormalAddress")) - self.gridLayout.addWidget(self.radioButtonBehaveNormalAddress, 0, 0, 1, 1) - self.radioButtonBehaviorMailingList = QtGui.QRadioButton(SpecialAddressBehaviorDialog) - self.radioButtonBehaviorMailingList.setObjectName(_fromUtf8("radioButtonBehaviorMailingList")) - self.gridLayout.addWidget(self.radioButtonBehaviorMailingList, 1, 0, 1, 1) - self.label = QtGui.QLabel(SpecialAddressBehaviorDialog) - self.label.setWordWrap(True) - self.label.setObjectName(_fromUtf8("label")) - self.gridLayout.addWidget(self.label, 2, 0, 1, 1) - self.label_2 = QtGui.QLabel(SpecialAddressBehaviorDialog) - self.label_2.setObjectName(_fromUtf8("label_2")) - self.gridLayout.addWidget(self.label_2, 3, 0, 1, 1) - self.lineEditMailingListName = QtGui.QLineEdit(SpecialAddressBehaviorDialog) - self.lineEditMailingListName.setEnabled(False) - self.lineEditMailingListName.setObjectName(_fromUtf8("lineEditMailingListName")) - self.gridLayout.addWidget(self.lineEditMailingListName, 4, 0, 1, 1) - self.buttonBox = QtGui.QDialogButtonBox(SpecialAddressBehaviorDialog) - 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(SpecialAddressBehaviorDialog) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), SpecialAddressBehaviorDialog.accept) - QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), SpecialAddressBehaviorDialog.reject) - QtCore.QObject.connect(self.radioButtonBehaviorMailingList, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setEnabled) - QtCore.QObject.connect(self.radioButtonBehaveNormalAddress, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setDisabled) - QtCore.QMetaObject.connectSlotsByName(SpecialAddressBehaviorDialog) - SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaveNormalAddress, self.radioButtonBehaviorMailingList) - SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaviorMailingList, self.lineEditMailingListName) - SpecialAddressBehaviorDialog.setTabOrder(self.lineEditMailingListName, self.buttonBox) - - def retranslateUi(self, SpecialAddressBehaviorDialog): - SpecialAddressBehaviorDialog.setWindowTitle(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Special Address Behavior", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonBehaveNormalAddress.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Behave as a normal address", None, QtGui.QApplication.UnicodeUTF8)) - self.radioButtonBehaviorMailingList.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Behave as a pseudo-mailing-list address", None, QtGui.QApplication.UnicodeUTF8)) - self.label.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public).", None, QtGui.QApplication.UnicodeUTF8)) - self.label_2.setText(QtGui.QApplication.translate("SpecialAddressBehaviorDialog", "Name of the pseudo-mailing-list:", None, QtGui.QApplication.UnicodeUTF8)) - +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'specialaddressbehavior.ui' +# +# Created: Wed Jul 10 15:20:02 2013 +# by: PyQt4 UI code generator 4.10.1 +# +# WARNING! All changes made in this file will be lost! + +from PyQt4 import QtCore, QtGui + +try: + _fromUtf8 = QtCore.QString.fromUtf8 +except AttributeError: + def _fromUtf8(s): + return s + +try: + _encoding = QtGui.QApplication.UnicodeUTF8 + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig, _encoding) +except AttributeError: + def _translate(context, text, disambig): + return QtGui.QApplication.translate(context, text, disambig) + +class Ui_SpecialAddressBehaviorDialog(object): + def setupUi(self, SpecialAddressBehaviorDialog): + SpecialAddressBehaviorDialog.setObjectName(_fromUtf8("SpecialAddressBehaviorDialog")) + SpecialAddressBehaviorDialog.resize(386, 186) + self.gridLayout = QtGui.QGridLayout(SpecialAddressBehaviorDialog) + self.gridLayout.setObjectName(_fromUtf8("gridLayout")) + self.radioButtonBehaveNormalAddress = QtGui.QRadioButton(SpecialAddressBehaviorDialog) + self.radioButtonBehaveNormalAddress.setChecked(True) + self.radioButtonBehaveNormalAddress.setObjectName(_fromUtf8("radioButtonBehaveNormalAddress")) + self.gridLayout.addWidget(self.radioButtonBehaveNormalAddress, 0, 0, 1, 1) + self.radioButtonBehaviorMailingList = QtGui.QRadioButton(SpecialAddressBehaviorDialog) + self.radioButtonBehaviorMailingList.setObjectName(_fromUtf8("radioButtonBehaviorMailingList")) + self.gridLayout.addWidget(self.radioButtonBehaviorMailingList, 1, 0, 1, 1) + self.label = QtGui.QLabel(SpecialAddressBehaviorDialog) + self.label.setWordWrap(True) + self.label.setObjectName(_fromUtf8("label")) + self.gridLayout.addWidget(self.label, 3, 0, 1, 1) + self.label_2 = QtGui.QLabel(SpecialAddressBehaviorDialog) + self.label_2.setObjectName(_fromUtf8("label_2")) + self.gridLayout.addWidget(self.label_2, 4, 0, 1, 1) + self.lineEditMailingListName = QtGui.QLineEdit(SpecialAddressBehaviorDialog) + self.lineEditMailingListName.setEnabled(False) + self.lineEditMailingListName.setObjectName(_fromUtf8("lineEditMailingListName")) + self.gridLayout.addWidget(self.lineEditMailingListName, 5, 0, 1, 1) + self.buttonBox = QtGui.QDialogButtonBox(SpecialAddressBehaviorDialog) + 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, 6, 0, 1, 1) + self.checkBoxWrapMessagesWithEmailHeaders = QtGui.QCheckBox(SpecialAddressBehaviorDialog) + self.checkBoxWrapMessagesWithEmailHeaders.setObjectName(_fromUtf8("checkBoxWrapMessagesWithEmailHeaders")) + self.gridLayout.addWidget(self.checkBoxWrapMessagesWithEmailHeaders, 2, 0, 1, 1) + + self.retranslateUi(SpecialAddressBehaviorDialog) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("accepted()")), SpecialAddressBehaviorDialog.accept) + QtCore.QObject.connect(self.buttonBox, QtCore.SIGNAL(_fromUtf8("rejected()")), SpecialAddressBehaviorDialog.reject) + QtCore.QObject.connect(self.radioButtonBehaviorMailingList, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setEnabled) + QtCore.QObject.connect(self.radioButtonBehaveNormalAddress, QtCore.SIGNAL(_fromUtf8("clicked(bool)")), self.lineEditMailingListName.setDisabled) + QtCore.QMetaObject.connectSlotsByName(SpecialAddressBehaviorDialog) + SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaveNormalAddress, self.radioButtonBehaviorMailingList) + SpecialAddressBehaviorDialog.setTabOrder(self.radioButtonBehaviorMailingList, self.lineEditMailingListName) + SpecialAddressBehaviorDialog.setTabOrder(self.lineEditMailingListName, self.buttonBox) + + def retranslateUi(self, SpecialAddressBehaviorDialog): + SpecialAddressBehaviorDialog.setWindowTitle(_translate("SpecialAddressBehaviorDialog", "Special Address Behavior", None)) + self.radioButtonBehaveNormalAddress.setText(_translate("SpecialAddressBehaviorDialog", "Behave as a normal address", None)) + self.radioButtonBehaviorMailingList.setText(_translate("SpecialAddressBehaviorDialog", "Behave as a pseudo-mailing-list address", None)) + self.label.setText(_translate("SpecialAddressBehaviorDialog", "Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public).", None)) + self.label_2.setText(_translate("SpecialAddressBehaviorDialog", "Name of the pseudo-mailing-list:", None)) + self.checkBoxWrapMessagesWithEmailHeaders.setText(_translate("SpecialAddressBehaviorDialog", "Ensure all incoming messages have standard E-mail headers", None)) + diff --git a/src/bitmessageqt/specialaddressbehavior.ui b/src/bitmessageqt/specialaddressbehavior.ui index eddc1184..8de5c528 100644 --- a/src/bitmessageqt/specialaddressbehavior.ui +++ b/src/bitmessageqt/specialaddressbehavior.ui @@ -7,7 +7,7 @@ 0 0 386 - 172 + 186 @@ -31,7 +31,7 @@ - + Mail received to a pseudo-mailing-list address will be automatically broadcast to subscribers (and thus will be public). @@ -41,21 +41,21 @@ - + Name of the pseudo-mailing-list: - + false - + @@ -71,6 +71,13 @@ + + + + Ensure all incoming messages have standard E-mail headers + + + diff --git a/src/class_pop3Server.py b/src/class_pop3Server.py index de27e5db..76e25358 100644 --- a/src/class_pop3Server.py +++ b/src/class_pop3Server.py @@ -293,7 +293,7 @@ class bitmessagePOP3Server(asyncore.dispatcher): _ = bitmessagePOP3Connection(sock, peer_address, debug=self.debug) @staticmethod - def reformatMessageForEmail(toAddress, fromAddress, body, subject): + def reformatMessageForReceipt(toAddress, fromAddress, body, subject): message = parser.Parser().parsestr(body) print(message) @@ -307,7 +307,7 @@ class bitmessagePOP3Server(asyncore.dispatcher): flags = tmp[i-21:i] checksum = int(flags[-4:], 16) - # Checksum to makesure incoming message hasn't been tampered with + # Checksum to make sure incoming message hasn't been tampered with c = hashlib.sha256(body).digest()[:2] c = (ord(checksum[0]) << 8) | ord(checksum[1]) @@ -338,9 +338,7 @@ class bitmessagePOP3Server(asyncore.dispatcher): message['From'] = fromLabel message['Date'] = utils.formatdate() - #if subject_is_valid and body_is_valid: - # return body, subject - + message['X-Bitmessage-Subject'] = subject if not subject_is_valid and 'Subject' not in message: message['Subject'] = subject @@ -377,4 +375,102 @@ class bitmessagePOP3Server(asyncore.dispatcher): subject = "".format(checksum) # Reserved flags. return message_as_text, subject - + + @staticmethod + def addMailingListNameToSubject(subject, mailingListName): + withoutre = subject = subject.strip() + re = '' + if subject[:3] == 'Re:' or subject[:3] == 'RE:': + re = subject[:3] + ' ' + withoutre = subject[3:].strip() + a = '[' + mailingListName + ']' + if withoutre.startswith(a): + return subject + else: + return re + a + ' ' + subject + + @staticmethod + def reformatMessageForMailingList(toAddress, fromAddress, body, subject, mailingListName): + message = parser.Parser().parsestr(body) + print(message) + + subject_is_valid = False + + i = subject.find('= 0: + tmp = subject[i:] + i = tmp.find('>') + if i >= 0: + flags = tmp[i-21:i] + checksum = int(flags[-4:], 16) + + # Checksum to make sure incoming message hasn't been tampered with + c = hashlib.sha256(body).digest()[:2] + c = (ord(checksum[0]) << 8) | ord(checksum[1]) + + # Valid Bitmessage subject line already + if c == checksum: + subject_is_valid = True + else: + with shared.printLock: + print 'Got E-Mail formatted message with incorrect checksum...' + + # The mailing list code will override some headers, including Date and From, so + # that the trust can be moved from the original sender to the mailing list owner. + fromLabel = '{}@{}'.format(getBase58Capitaliation(fromAddress), fromAddress) + + if 'From' in message: + originalFrom = message['From'] + message['X-Original-From'] = originalFrom + + i = originalFrom.find('<' + fromLabel + '>') + if i >= 0: + fromLabel = '{} <{}>'.format(originalFrom[:i].strip(), fromLabel) + + message['From'] = fromLabel + message['Date'] = utils.formatdate() + + message['X-Bitmessage-Subject'] = subject + if 'Subject' not in message: + if not subject_is_valid: + message['Subject'] = bitmessagePOP3Server.addMailingListNameToSubject(subject, mailingListName) + else: + # TODO - strip from bitmessage subject? + message['Subject'] = bitmessagePOP3Server.addMailingListNameToSubject('', mailingListName) + else: + message['Subject'] = bitmessagePOP3Server.addMailingListNameToSubject(message['Subject'], mailingListName) + + toLabel = '{}@{}'.format(getBase58Capitaliation(toAddress), toAddress) + try: + toLabel = '{} <{}>'.format(shared.config.get(toAddress, 'label'), toLabel) + except: + pass + + if 'To' in message: + message['X-Original-To'] = message['To'] + message['To'] = toLabel + + # X-Bitmessage-MailingList-Name + message['X-Bitmessage-MailingList-Name'] = mailingListName + + # X-Bitmessage-MailingList-Address + mailingListAddress = "{}@{}".format(getBase58Capitaliation(toAddress), toAddress) + message['X-Bitmessage-MailingList-Address'] = mailingListAddress + + # X-Bitmessage-MailingList-Version + message["X-Bitmessage-MailingList-Version"] = shared.softwareVersion + + fp = StringIO() + gen = generator.Generator(fp, mangle_from_=False, maxheaderlen=128) + gen.flatten(message) + + message_as_text = fp.getvalue() + + # Checksum to makesure incoming message hasn't been tampered with + # TODO - if subject_is_valid, then don't completely overwrite the subject, instead include all the data outside of <> too + checksum = hashlib.sha256(message_as_text).digest()[:2] + checksum = (ord(checksum[0]) << 8) | ord(checksum[1]) + subject = bitmessagePOP3Server.addMailingListNameToSubject("".format(checksum), mailingListAddress) + + return message_as_text, subject + diff --git a/src/class_receiveDataThread.py b/src/class_receiveDataThread.py index e97bdd4b..8fee0ad5 100644 --- a/src/class_receiveDataThread.py +++ b/src/class_receiveDataThread.py @@ -1031,17 +1031,18 @@ class receiveDataThread(threading.Thread): body = 'Unknown encoding type.\n\n' + repr(message) subject = '' + formattedBody, formattedSubject = body, subject if isEmailAddress: # The above 'message/subject' formatting may give us weird values if messageEncodingType is bad - body, subject = bitmessagePOP3Server.reformatMessageForEmail(toAddress, fromAddress, body, subject) + formattedBody, formattedSubject = bitmessagePOP3Server.reformatMessageForReceipt(toAddress, fromAddress, body, subject) if messageEncodingType != 0: - t = (self.inventoryHash, toAddress, fromAddress, subject, int( - time.time()), body, 'inbox', messageEncodingType, 0) + t = (self.inventoryHash, toAddress, fromAddress, formattedSubject, int( + time.time()), formattedBody, 'inbox', messageEncodingType, 0) helper_inbox.insert(t) shared.UISignalQueue.put(('displayNewInboxMessage', ( - self.inventoryHash, toAddress, fromAddress, subject, body))) + self.inventoryHash, toAddress, fromAddress, formattedSubject, formattedBody))) # If we are behaving as an API then we might need to run an # outside command to let some program know that a new message @@ -1063,12 +1064,18 @@ class receiveDataThread(threading.Thread): toAddress, 'mailinglistname') except: mailingListName = '' - # Let us send out this message as a broadcast - subject = self.addMailingListNameToSubject( - subject, mailingListName) - # Let us now send this message out as a broadcast - message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( - )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body + + if isEmailAddress: + # The above 'message/subject' formatting may give us weird values if messageEncodingType is bad + message, subject = bitmessagePOP3Server.reformatMessageForMailingList(toAddress, fromAddress, body, subject, mailingListName) + else: + # Let us send out this message as a broadcast + subject = self.addMailingListNameToSubject( + subject, mailingListName) + # Let us now send this message out as a broadcast + message = time.strftime("%a, %Y-%m-%d %H:%M:%S UTC", time.gmtime( + )) + ' Message ostensibly from ' + fromAddress + ':\n\n' + body + fromAddress = toAddress # The fromAddress for the broadcast that we are about to send is the toAddress (my address) for the msg message we are currently processing. ackdata = OpenSSL.rand( 32) # We don't actually need the ackdata for acknowledgement since this is a broadcast message but we can use it to update the user interface when the POW is done generating.