class_smtpServer.py.bak
1 """ 2 SMTP server thread 3 """ 4 import asyncore 5 import base64 6 import email 7 import logging 8 import re 9 import signal 10 import smtpd 11 import threading 12 import time 13 from email.header import decode_header 14 from email.parser import Parser 15 16 import queues 17 from addresses import decodeAddress 18 from bmconfigparser import config 19 from helper_ackPayload import genAckPayload 20 from helper_sql import sqlExecute 21 from network.threads import StoppableThread 22 from version import softwareVersion 23 24 SMTPDOMAIN = "bmaddr.lan" 25 LISTENPORT = 8425 26 27 logger = logging.getLogger('default') 28 # pylint: disable=attribute-defined-outside-init 29 30 31 class SmtpServerChannelException(Exception): 32 """Generic smtp server channel exception.""" 33 pass 34 35 36 class smtpServerChannel(smtpd.SMTPChannel): 37 """Asyncore channel for SMTP protocol (server)""" 38 def smtp_EHLO(self, arg): 39 """Process an EHLO""" 40 if not arg: 41 self.push('501 Syntax: HELO hostname') 42 return 43 self.push('250-PyBitmessage %s' % softwareVersion) 44 self.push('250 AUTH PLAIN') 45 46 def smtp_AUTH(self, arg): 47 """Process AUTH""" 48 if not arg or arg[0:5] not in ["PLAIN"]: 49 self.push('501 Syntax: AUTH PLAIN') 50 return 51 authstring = arg[6:] 52 try: 53 decoded = base64.b64decode(authstring) 54 correctauth = "\x00" + config.safeGet( 55 "bitmessagesettings", "smtpdusername", "") + "\x00" + config.safeGet( 56 "bitmessagesettings", "smtpdpassword", "") 57 logger.debug('authstring: %s / %s', correctauth, decoded) 58 if correctauth == decoded: 59 self.auth = True 60 self.push('235 2.7.0 Authentication successful') 61 else: 62 raise SmtpServerChannelException("Auth fail") 63 except: # noqa:E722 64 self.push('501 Authentication fail') 65 66 def smtp_DATA(self, arg): 67 """Process DATA""" 68 if not hasattr(self, "auth") or not self.auth: 69 self.push('530 Authentication required') 70 return 71 smtpd.SMTPChannel.smtp_DATA(self, arg) 72 73 74 class smtpServerPyBitmessage(smtpd.SMTPServer): 75 """Asyncore SMTP server class""" 76 def handle_accept(self): 77 """Accept a connection""" 78 pair = self.accept() 79 if pair is not None: 80 conn, addr = pair 81 self.channel = smtpServerChannel(self, conn, addr) 82 83 def send(self, fromAddress, toAddress, subject, message): 84 """Send a bitmessage""" 85 # pylint: disable=arguments-differ 86 streamNumber, ripe = decodeAddress(toAddress)[2:] 87 stealthLevel = config.safeGetInt('bitmessagesettings', 'ackstealthlevel') 88 ackdata = genAckPayload(streamNumber, stealthLevel) 89 sqlExecute( 90 '''INSERT INTO sent VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)''', 91 '', 92 toAddress, 93 ripe, 94 fromAddress, 95 subject, 96 message, 97 ackdata, 98 int(time.time()), # sentTime (this will never change) 99 int(time.time()), # lastActionTime 100 0, # sleepTill time. This will get set when the POW gets done. 101 'msgqueued', 102 0, # retryNumber 103 'sent', # folder 104 2, # encodingtype 105 # not necessary to have a TTL higher than 2 days 106 min(config.getint('bitmessagesettings', 'ttl'), 86400 * 2) 107 ) 108 109 queues.workerQueue.put(('sendmessage', toAddress)) 110 111 def decode_header(self, hdr): 112 """Email header decoding""" 113 ret = [] 114 for h in decode_header(self.msg_headers[hdr]): 115 if h[1]: 116 ret.append(h[0].decode(h[1])) 117 else: 118 ret.append(h[0].decode("utf-8", errors='replace')) 119 120 return ret 121 122 def process_message(self, peer, mailfrom, rcpttos, data): 123 """Process an email""" 124 # pylint: disable=too-many-locals, too-many-branches 125 p = re.compile(".*<([^>]+)>") 126 if not hasattr(self.channel, "auth") or not self.channel.auth: 127 logger.error('Missing or invalid auth') 128 return 129 try: 130 self.msg_headers = Parser().parsestr(data) 131 except: # noqa:E722 132 logger.error('Invalid headers') 133 return 134 135 try: 136 sender, domain = p.sub(r'\1', mailfrom).split("@") 137 if domain != SMTPDOMAIN: 138 raise Exception("Bad domain %s" % domain) 139 if sender not in config.addresses(): 140 raise Exception("Nonexisting user %s" % sender) 141 except Exception as err: 142 logger.debug('Bad envelope from %s: %r', mailfrom, err) 143 msg_from = self.decode_header("from") 144 try: 145 msg_from = p.sub(r'\1', self.decode_header("from")[0]) 146 sender, domain = msg_from.split("@") 147 if domain != SMTPDOMAIN: 148 raise Exception("Bad domain %s" % domain) 149 if sender not in config.addresses(): 150 raise Exception("Nonexisting user %s" % sender) 151 except Exception as err: 152 logger.error('Bad headers from %s: %r', msg_from, err) 153 return 154 155 try: 156 msg_subject = self.decode_header('subject')[0] 157 except: # noqa:E722 158 msg_subject = "Subject missing..." 159 160 msg_tmp = email.message_from_string(data) 161 body = u'' 162 for part in msg_tmp.walk(): 163 if part and part.get_content_type() == "text/plain": 164 body += part.get_payload(decode=1).decode(part.get_content_charset('utf-8'), errors='replace') 165 166 for to in rcpttos: 167 try: 168 rcpt, domain = p.sub(r'\1', to).split("@") 169 if domain != SMTPDOMAIN: 170 raise Exception("Bad domain %s" % domain) 171 logger.debug( 172 'Sending %s to %s about %s', sender, rcpt, msg_subject) 173 self.send(sender, rcpt, msg_subject, body) 174 logger.info('Relayed %s to %s', sender, rcpt) 175 except Exception as err: 176 logger.error('Bad to %s: %r', to, err) 177 continue 178 return 179 180 181 class smtpServer(StoppableThread): 182 """SMTP server thread""" 183 def __init__(self, _=None): 184 super(smtpServer, self).__init__(name="smtpServerThread") 185 self.server = smtpServerPyBitmessage(('127.0.0.1', LISTENPORT), None) 186 187 def stopThread(self): 188 super(smtpServer, self).stopThread() 189 self.server.close() 190 return 191 192 def run(self): 193 asyncore.loop(1) 194 195 196 def signals(_, __): 197 """Signal handler""" 198 logger.warning('Got signal, terminating') 199 for thread in threading.enumerate(): 200 if thread.isAlive() and isinstance(thread, StoppableThread): 201 thread.stopThread() 202 203 204 def runServer(): 205 """Run SMTP server as a standalone python process""" 206 logger.warning('Running SMTPd thread') 207 smtpThread = smtpServer() 208 smtpThread.start() 209 signal.signal(signal.SIGINT, signals) 210 signal.signal(signal.SIGTERM, signals) 211 logger.warning('Processing') 212 smtpThread.join() 213 logger.warning('The end') 214 215 216 if __name__ == "__main__": 217 runServer()