summaryrefslogtreecommitdiff
path: root/smtp.py
blob: b1b2e676806b18c9e7a1ca7b1ec2ba6b6b9082ec (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import asynchat, asyncore, socket
import pwd, os, sys
import email
from mailbox import Maildir,MaildirMessage

class SMTPChannel(asynchat.async_chat):
    def __init__(self, server, sock, addr):
        asynchat.async_chat.__init__(self, sock)
        self.server = server
        self.set_terminator("\n")
        self.data = ""
        self.read_data = False

    def collect_incoming_data(self, data):
        if len(self.data) > 0:
            self.data += "\n"
        self.data += data
        if self.data.__len__() > 16384:
            print "too much data, shutdown"
            self.close_when_done()

    def found_terminator(self):
        print self.data
        if self.read_data:
            if self.data.endswith("\n."):
                self.push("250 Ok: queued as 12345\n")
                self.read_data = False
                mail = email.message_from_string(self.data)
                self.server.maildir.add(mail)
                self.server.maildir.flush()
                self.data = ""
                
        elif self.data.startswith("EHLO") \
                or self.data.startswith("HELO"):
            self.push("HELO there\n")
            self.data = ""
        elif self.data.startswith("DATA"):
            self.push("354 End data with <CR><LF>.<CR><LF>\n")
            self.read_data = True
            self.data=""
        elif self.data.startswith("QUIT"):
            self.data = ""
            self.push("221 Bye\n")
            self.close_when_done()
        else:
            self.push("250 OK\n")
            self.data = ""
            

class SMTPServer(asyncore.dispatcher):
    def __init__(self):
        asyncore.dispatcher.__init__(self)
        self.port = 25
        self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
        self.set_reuse_addr()
        self.bind(("", self.port))
        self.listen(5)
        self.maildir = Maildir("Maildir", create=True)

    def handle_accept(self):
        conn, addr = self.accept()
        SMTPChannel(self, conn, addr)



if __name__ == '__main__':
    s = SMTPServer()

    #user change
    pwinfo = pwd.getpwnam('nobody')
    os.setregid(pwinfo[3],pwinfo[3])
    os.setreuid(pwinfo[2],pwinfo[2])

    #"Robustly turn into a UNIX daemon, running in our_home_dir."
    # First fork
    try:
        pid = os.fork()
        if pid > 0:
            print "PID: %s" % pid
            sys.exit(0)     # kill off parent
    except OSError, e:
        sys.stderr.write("fork #1 failed: (%d) %s\n" % (e.errno, e.strerror))
        sys.exit(1)
    os.setsid()
    os.chdir('/')
    os.umask(0)

    # Second fork
    try:
        if os.fork() > 0:
            os._exit(0)
    except OSError, e:
        sys.stderr.write("fork #2 failed: (%d) %s\n" % (e.errno, e.strerror))
        os._exit(1)

    si = open('/dev/null', 'r')
    so = open('/dev/null', 'a+', 0)
    se = open('/dev/null', 'a+', 0)
    os.dup2(si.fileno(), sys.stdin.fileno())
    os.dup2(so.fileno(), sys.stdout.fileno())
    os.dup2(se.fileno(), sys.stderr.fileno())
    # Set custom file descriptors so that they get proper buffering.
    sys.stdout, sys.stderr = so, se
 
    asyncore.loop()