2013-07-03 16:09:53 +02:00
from pyelliptic . openssl import OpenSSL
import shared
import smtpd
2013-07-03 17:30:06 +02:00
import ssl
2013-07-03 16:09:53 +02:00
import time
from addresses import *
import helper_sent
class bitmessageSMTPServer ( smtpd . SMTPServer ) :
def __init__ ( self ) :
# TODO - move to separate file/class
smtpport = shared . config . getint ( ' bitmessagesettings ' , ' smtpport ' )
2013-07-03 17:30:06 +02:00
self . ssl = shared . config . getboolean ( ' bitmessagesettings ' , ' smtpssl ' )
if self . ssl :
self . keyfile = shared . config . get ( ' bitmessagesettings ' , ' keyfile ' )
self . certfile = shared . config . get ( ' bitmessagesettings ' , ' certfile ' )
smtpd . SMTPServer . __init__ ( self , ( ' 127.0.0.1 ' , smtpport ) , None )
2013-07-03 16:09:53 +02:00
shared . printLock . acquire ( )
print " SMTP server started "
shared . printLock . release ( )
2013-07-03 17:30:06 +02:00
def handle_accept ( self ) :
# Override SMTPServer's handle_accept so that we can start an SSL connection.
if not self . ssl :
return smtpd . SMTPServer . handle_accept ( self )
sock , peer_address = self . accept ( )
sock = ssl . wrap_socket ( sock , server_side = True , certfile = self . certfile , keyfile = self . keyfile , ssl_version = ssl . PROTOCOL_SSLv23 )
channel = smtpd . SMTPChannel ( self , sock , peer_address )
2013-07-03 16:09:53 +02:00
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 = " <Bitmessage Mail: 00000000000000000000> " # 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') ?