summaryrefslogtreecommitdiff
path: root/server_sendmemo.py
blob: 1a7d0932c5390283818d085342d274d4c3932494 (plain) (blame)
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
#!/usr/bin/env python3
#
# Send the memo in standard input to a memo server.
#
# This work is in the public domain.  See the NOCOPYRIGHT file in this
# repository for details.
#

from ememo_common import parse_header_line, parse_address
import sys
import socket
import certifi
import ssl
from time import time

PORT = 6935
# This is currently hardcoded, we might use SRV records in the future.
# That would put some dependency on DNS but that shouldn't be an issue.

client_cert = "fullchain.pem"
client_key = "privkey.pem"

context = ssl.create_default_context(cafile=certifi.where())
context.load_cert_chain(certfile=client_cert, keyfile=client_key)

if len(sys.argv) >= 2:
    with open(sys.argv[1], "rb") as f:
        d = f.read()
else:
    d = sys.stdin.buffer.read()

header, data_and_beyond = d.split(b"\n\n", 1)
headers = {}
for line in header.split(b"\n"):
    k, v = parse_header_line(line)
    if k is Exception:
        print(v.decode("utf-8"))
        sys.exit(1)
    headers[k] = v
headers[b"send-server-time"] = str(time()).encode("ascii")

to = headers.get(b"to", None)
cc = headers.get(b"cc", None)
bcc = headers.get(b"bcc", None)

l_to = [parse_address(a) for a in to.split(b", ")] if to else []
l_cc = [parse_address(a) for a in cc.split(b", ")] if cc else []
l_bcc = [parse_address(a) for a in bcc.split(b", ")] if bcc else []

bccs_by_server = {}

for r in l_to + l_cc:
    if not r[2] in bccs_by_server:
        bccs_by_server[r[2]] = []

for r in l_bcc:
    if not r[2] in bccs_by_server:
        bccs_by_server[r[2]] = [r]
    else:
        bccs_by_server[r[2]].append(r)

bcc_header_by_server = {}
for server_addr_b in bccs_by_server:
    if not bccs_by_server[server_addr_b]:
        bcc_header_by_server[server_addr_b] = None
    else:
        bcc_header_by_server[server_addr_b] = b", ".join(
            [
                (
                    b"%s <%s@%s>" % (r[0], r[1], r[2])
                    if r[0]
                    else b"%s@%s" % (r[1], r[2])
                )
                for r in bccs_by_server[server_addr_b]
            ]
        )

sbs = {}
for server_addr_b in bcc_header_by_server:
    server_addr = server_addr_b.decode("utf-8", "surrogateescape")
    sendbuf = b""
    for k, v in headers.items():
        if k != b"bcc":
            sendbuf += b"%s: %s\n" % (k, v)
        elif k == b"bcc" and bcc_header_by_server[server_addr_b]:
            sendbuf += b"bcc: %s\n" % bcc_header_by_server[server_addr_b]
    sendbuf += b"\n"
    sendbuf += data_and_beyond
    sbs[server_addr] = sendbuf


for server_addr, sendbuf in sbs.items():
    print(server_addr)
    conn = context.wrap_socket(
        socket.socket(socket.AF_INET, socket.SOCK_STREAM),
        server_hostname=server_addr,
    )
    conn.connect((server_addr, PORT))
    conn.send(b"MAIL %d\n" % len(sendbuf))
    conn.send(sendbuf)
    s = conn.recv(512)
    sys.stdout.buffer.write(s)
    conn.send(b"BYE\n")

    while True:
        s = conn.recv(512)
        if s == b"":
            break
        sys.stdout.buffer.write(s)
    conn.shutdown(socket.SHUT_RDWR)
    conn.close()