File: courier.py

package info (click to toggle)
twisted 25.5.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 20,560 kB
  • sloc: python: 203,171; makefile: 200; sh: 92; javascript: 36; xml: 31
file content (127 lines) | stat: -rw-r--r-- 2,992 bytes parent folder | download | duplicates (3)
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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python

# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
Example of an interface to Courier's mail filter.
"""

LOGFILE = "/tmp/filter.log"

# Setup log file
from twisted.python import log

log.startLogging(open(LOGFILE, "a"))
import sys

sys.stderr = log.logfile

# Twisted imports
from twisted.internet import reactor, stdio
from twisted.internet.protocol import Factory, Protocol
from twisted.protocols import basic

FILTERS = "/var/lib/courier/filters"
ALLFILTERS = "/var/lib/courier/allfilters"
FILTERNAME = "twistedfilter"

import email.message
import email.parser
import os
import os.path
from syslog import LOG_MAIL, openlog, syslog


def trace_dump():
    t, v, tb = sys.exc_info()
    openlog(FILTERNAME, 0, LOG_MAIL)
    syslog(f"Unhandled exception: {v} - {t}")
    while tb:
        syslog(
            "Trace: {}:{} {}".format(
                tb.tb_frame.f_code.co_filename, tb.tb_frame.f_code.co_name, tb.tb_lineno
            )
        )
        tb = tb.tb_next
    # just to be safe
    del tb


def safe_del(file):
    try:
        if os.path.isdir(file):
            os.removedirs(file)
        else:
            os.remove(file)
    except OSError:
        pass


class DieWhenLost(Protocol):
    def connectionLost(self, reason=None):
        reactor.stop()


class MailProcessor(basic.LineReceiver):
    """
    I process a mail message.

    Override filterMessage to do any filtering you want.
    """

    messageFilename = None
    delimiter = "\n"

    def connectionMade(self):
        log.msg(f"Connection from {self.transport}")
        self.state = "connected"
        self.metaInfo = []

    def lineReceived(self, line):
        if self.state == "connected":
            self.messageFilename = line
            self.state = "gotMessageFilename"
        if self.state == "gotMessageFilename":
            if line:
                self.metaInfo.append(line)
            else:
                if not self.metaInfo:
                    self.transport.loseConnection()
                    return
                self.filterMessage()

    def filterMessage(self):
        """Override this.

        A trivial example is included.
        """
        try:
            emailParser = email.parser.Parser()
            with open(self.messageFilename) as f:
                emailParser.parse(f)
            self.sendLine(b"200 Ok")
        except BaseException:
            trace_dump()
            self.sendLine(b"435 " + FILTERNAME.encode("ascii") + b" processing error")


def main():
    # Listen on the UNIX socket
    f = Factory()
    f.protocol = MailProcessor
    safe_del(f"{ALLFILTERS}/{FILTERNAME}")
    reactor.listenUNIX(f"{ALLFILTERS}/{FILTERNAME}", f, 10)

    # Once started, close fd 3 to let Courier know we're ready
    reactor.callLater(0, os.close, 3)

    # When stdin is closed, it's time to exit.
    stdio.StandardIO(DieWhenLost())

    # Go!
    reactor.run()


if __name__ == "__main__":
    main()