From f482a4bc4cceccd2bc0e9696baf4d6ed02a646d9 Mon Sep 17 00:00:00 2001 From: Chuck Date: Wed, 3 Jul 2013 22:06:37 +0700 Subject: [PATCH] Adding refactored changes --- src/bitmessagemain.py | 2 + src/class_asyncoreThread.py | 369 ------------------------------------ 2 files changed, 2 insertions(+), 369 deletions(-) diff --git a/src/bitmessagemain.py b/src/bitmessagemain.py index e8eb749d..dc6615ee 100644 --- a/src/bitmessagemain.py +++ b/src/bitmessagemain.py @@ -25,6 +25,8 @@ from class_outgoingSynSender import * from class_singleListener import * from class_addressGenerator import * from class_asyncoreThread import * +from class_smtpServer import * +from class_pop3Server import * # Helper Functions import helper_startup diff --git a/src/class_asyncoreThread.py b/src/class_asyncoreThread.py index 210f9f6e..74b1be6c 100644 --- a/src/class_asyncoreThread.py +++ b/src/class_asyncoreThread.py @@ -1,375 +1,6 @@ -from collections import deque -from pyelliptic.openssl import OpenSSL import asyncore import shared -import smtpd -import socket -import sys import threading -import time -import traceback - -from addresses import * -import helper_inbox -import helper_sent - -class bitmessageSMTPServer(smtpd.SMTPServer): - def __init__(self): - # TODO - move to separate file/class - smtpport = shared.config.getint('bitmessagesettings', 'smtpport') - smtpd.SMTPServer.__init__(self, ('127.0.0.1', smtpport), None) - - shared.printLock.acquire() - print "SMTP server started" - shared.printLock.release() - - def process_message(self, peer, mailfrom, rcpttos, data): - #print("Peer", peer) - #print("Mail From", mailfrom) - #print("Rcpt To", rcpttos) - #print("Data") - #print(data) - #print('--------') - #print(type(mailfrom)) - - message = data - - # Determine the fromAddress and make sure it's an owned identity - # TODO - determine the address from a SMTP authorization. - # TODO - use the mailfrom (a legitimate email address?) when delivering - # real e-mail. - _, fromAddress = mailfrom.split('@', 1) - if not (fromAddress.startswith('BM-') and '.' not in fromAddress): - raise Exception("From Address must be a Bitmessage address.") - else: - status, addressVersionNumber, streamNumber, fromRipe = decodeAddress(fromAddress) - if status != 'success': - shared.printLock.acquire() - print 'Error: Could not decode address: ' + fromAddress + ' : ' + status - if status == 'checksumfailed': - print 'Error: Checksum failed for address: ' + fromAddress - if status == 'invalidcharacters': - print 'Error: Invalid characters in address: ' + fromAddress - if status == 'versiontoohigh': - print 'Error: Address version number too high (or zero) in address: ' + fromAddress - shared.printLock.release() - raise Exception("Invalid Bitmessage address: {}".format(fromAddress)) - #fromAddress = addBMIfNotPresent(fromAddress) # I know there's a BM-, because it's required when using SMTP - - try: - fromAddressEnabled = shared.config.getboolean(fromAddress, 'enabled') - except: - shared.printLock.acquire() - print 'Error: Could not find your fromAddress in the keys.dat file.' - shared.printLock.release() - raise Exception("Could not find address in keys.dat: {}".format(fromAddress)) - if not fromAddressEnabled: - shared.printLock.acquire() - print 'Error: Your fromAddress is disabled. Cannot send.' - shared.printLock.release() - raise Exception("The fromAddress is disabled: {}".format(fromAddress)) - - for recipient in rcpttos: - _, toAddress = recipient.split('@', 1) - if not (toAddress.startswith('BM-') and '.' not in toAddress): - # TODO - deliver message to another SMTP server.. ? - raise Exception("Cannot yet handle normal E-mail addresses.") - else: - # This is now the 3rd copy of this code. There's one in the API, there's another - # copy in __init__ for the UI. Yet another exists here. It needs to be refactored - # into a utility func! - status, addressVersionNumber, streamNumber, toRipe = decodeAddress(toAddress) - if status != 'success': - shared.printLock.acquire() - print 'Error: Could not decode address: ' + toAddress + ' : ' + status - if status == 'checksumfailed': - print 'Error: Checksum failed for address: ' + toAddress - if status == 'invalidcharacters': - print 'Error: Invalid characters in address: ' + toAddress - if status == 'versiontoohigh': - print 'Error: Address version number too high (or zero) in address: ' + toAddress - shared.printLock.release() - raise Exception("Invalid Bitmessage address: {}".format(toAddress)) - #toAddress = addBMIfNotPresent(toAddress) # I know there's a BM-, because it's required when using SMTP - - toAddressIsOK = False - try: - shared.config.get(toAddress, 'enabled') - # The toAddress is one owned by me. We cannot send - # messages to ourselves without significant changes - # to the codebase. - shared.printLock.acquire() - print "Error: One of the addresses to which you are sending a message, {}, is yours. Unfortunately the Bitmessage client cannot process its own messages. Please try running a second client on a different computer or within a VM.".format(toAddress) - shared.printLock.release() - except: - toAddressIsOK = True - - if not toAddressIsOK: - raise Exception("Cannot send message to {}".format(toAddress)) - - # The subject is specially formatted to identify it from non-E-mail messages. - subject = "" # Reserved, flags. - - ackdata = OpenSSL.rand(32) - t = ('', toAddress, toRipe, fromAddress, subject, message, ackdata, int(time.time()), 'msgqueued', 1, 1, 'sent', 2) - helper_sent.insert(t) - - toLabel = '' - t = (toAddress,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''select label from addressbook where address=?''') - shared.sqlSubmitQueue.put(t) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - if queryreturn != []: - for row in queryreturn: - toLabel, = row - shared.UISignalQueue.put(('displayNewSentMessage', (toAddress, toLabel, fromAddress, subject, message, ackdata))) - shared.workerQueue.put(('sendmessage', toAddress)) - - # TODO - what should we do with ackdata.encode('hex') ? - -class bitmessagePOP3Connection(asyncore.dispatcher): - END = b"\r\n" - - def __init__(self, sock, peer_address, debug=False): - asyncore.dispatcher.__init__(self, sock) - self.peer_address = peer_address - self.data_buffer = [] - self.commands = deque() - self.debug = debug - - self.dispatch = dict( - USER=self.handleUser, - PASS=self.handlePass, - STAT=self.handleStat, - LIST=self.handleList, - #TOP=self.handleTop, - RETR=self.handleRetr, - DELE=self.handleDele, - NOOP=self.handleNoop, - QUIT=self.handleQuit, - ) - - #self.bitmessage = Bitmessage() - self.messages = None #TODO self.bitmessage.getInboxMessagesByAddress(BITMAIL_ADDRESS) - self.storage_size = 0 - self.address = None - - #for msg in self.messages: - # msg['message'] = base64.b64decode(msg['message'].encode('ascii')) - # msg['subject'] = base64.b64decode(msg['subject'].encode('ascii')) - # msg['size'] = len(msg['message']) - # self.storage_size += msg['size'] - - #print(self.messages) - - self.sendline("+OK Bitmessage POP3 server ready") - - def populateMessageIndex(self): - if self.address is None: - raise Exception("Invalid address: {}".format(self.address)) - - if self.messages is not None: - return - - v = (self.address,) - shared.sqlLock.acquire() - # TODO LENGTH(message) needs to be the byte-length, not the character-length. - shared.sqlSubmitQueue.put('''SELECT msgid, fromaddress, subject, LENGTH(message) FROM inbox WHERE folder='inbox' AND toAddress=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - - self.storage_size = 0 - self.messages = [] - for row in queryreturn: - msgid, fromAddress, subject, size = row - subject = shared.fixPotentiallyInvalidUTF8Data(subject) - if subject.startswith("': - subject = "" # Reserved, flags. - flags = subject[-21:-1] - # TODO - checksum? - - self.messages.append({ - 'msgid': msgid, - 'fromAddress': fromAddress, - 'subject': subject, - 'size': size, - }) - - self.storage_size += size - - - def getMessageContent(self, msgid): - if self.address is None: - raise Exception("Invalid address: {}".format(self.address)) - - v = (msgid,) - shared.sqlLock.acquire() - shared.sqlSubmitQueue.put('''SELECT fromaddress, received, message, encodingtype FROM inbox WHERE msgid=?''') - shared.sqlSubmitQueue.put(v) - queryreturn = shared.sqlReturnQueue.get() - shared.sqlLock.release() - - for row in queryreturn: - fromAddress, received, message, encodingtype = row - message = shared.fixPotentiallyInvalidUTF8Data(message) - return { - 'fromAddress': fromAddress, - 'received': received, - 'message': message, - 'encodingtype': encodingtype - } - - def trashMessage(self, msgid): - # TODO - how to determine if the update succeeded? - if not self.debug: - helper_inbox.trash(msgid) - return True - - def sendline(self, data, END=END): - if self.debug: - shared.printLock.acquire() - sys.stdout.write("sending ") - sys.stdout.write(data) - sys.stdout.write("\n") - shared.printLock.release() - data = data + END - while len(data) > 4096: - self.send(data[:4096]) - data = data[4096:] - if len(data): - self.send(data) - - def handle_read(self): - chunk = self.recv(4096) - - while bitmessagePOP3Connection.END in chunk: - # Join all the data up to the END and throw it in commands - command = b''.join(self.data_buffer) + chunk[:chunk.index(bitmessagePOP3Connection.END)] - chunk = chunk[chunk.index(bitmessagePOP3Connection.END)+2:] - self.data_buffer = [] - self.commands.append(command) - - if len(chunk): - self.data_buffer.append(chunk) - - if self.debug: - shared.printLock.acquire() - print('data_buffer', self.data_buffer) - print('commands', self.commands) - print('-') - shared.printLock.release() - - while len(self.commands): - line = self.commands.popleft() - - if b' ' in line: - cmd, data = line.split(b' ', 1) - else: - cmd, data = line, b'' - - try: - cmd = self.dispatch[cmd.decode('ascii').upper()] - except KeyError: - self.sendline('-ERR unknown command') - continue - - for response in cmd(data): - self.sendline(response) - - if cmd is self.handleQuit: - self.close() - break - - def handleUser(self, data): - self.address = data - - status, addressVersionNumber, streamNumber, ripe = decodeAddress(self.address) - if status != 'success': - shared.printLock.acquire() - print 'Error: Could not decode address: ' + self.address + ' : ' + status - if status == 'checksumfailed': - print 'Error: Checksum failed for address: ' + self.address - if status == 'invalidcharacters': - print 'Error: Invalid characters in address: ' + self.address - if status == 'versiontoohigh': - print 'Error: Address version number too high (or zero) in address: ' + self.address - shared.printLock.release() - raise Exception("Invalid Bitmessage address: {}".format(self.address)) - - return ["+OK user accepted"] - - def handlePass(self, data): - # TODO - return ["+OK pass accepted"] - - def handleStat(self, data): - self.populateMessageIndex() - return ["+OK {} {}".format(len(self.messages), self.storage_size)] - - def handleList(self, data): - self.populateMessageIndex() - yield "+OK {} messages ({} octets)".format(len(self.messages), self.storage_size) - for i, msg in enumerate(self.messages): - yield "{} {}".format(i + 1, msg['size']) - yield "." - - #def handleTop(self, data): - # cmd, num, lines = data.split() - # assert num == "1", "unknown message number: {}".format(num) - # lines = int(lines) - # text = msg.top + "\r\n\r\n" + "\r\n".join(msg.bot[:lines]) - # return "+OK top of message follows\r\n{}\r\n.".format(text) - - def handleRetr(self, data): - index = int(data.decode('ascii')) - 1 - assert index >= 0 - self.populateMessageIndex() - msg = self.messages[index] - content = self.getMessageContent(msg['msgid']) - if self.debug: - shared.printLock.acquire() - sys.stdout.write(str(msg) + ": " + str(content)) - shared.printLock.release() - yield "+OK {} octets".format(msg['size']) - yield content['message'] - yield '.' - - def handleDele(self, data): - index = int(data.decode('ascii')) - 1 - assert index >= 0 - self.populateMessageIndex() - msg = self.messages[index] - if self.trashMessage(msg['msgid']): - return ["+OK deleted"] - else: - return ["-ERR internal error"] - - def handleNoop(self, data): - return ["+OK"] - - def handleQuit(self, data): - return ["+OK Bitmessage POP3 server signing off"] - -class bitmessagePOP3Server(asyncore.dispatcher): - def __init__(self, debug=False): - asyncore.dispatcher.__init__(self) - self.debug = debug - - pop3port = shared.config.getint('bitmessagesettings', 'pop3port') - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.bind(('127.0.0.1', pop3port)) - self.listen(10) - - shared.printLock.acquire() - print "POP3 server started" - shared.printLock.release() - - def handle_accept(self): - sock, peer_address = self.accept() - _ = bitmessagePOP3Connection(sock, peer_address, debug=self.debug) class asyncoreThread(threading.Thread):