-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.ts
133 lines (123 loc) · 3.47 KB
/
main.ts
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
128
129
130
131
132
133
// Title : SMTP Server
// Description: This implement a basic SMTP server
// Author: Nassim Zenzelaoui
import { commandMap } from "./Server/Command.ts";
import { SMTPCommand } from "./Server/Interfaces.ts";
import { Security } from "./Server/Security.ts";
import { ClientConn, SessionState } from "./Server/Session.ts";
import { SMTPServer } from "./Server/Smtp.ts";
try {
Deno.statSync("./server.key");
} catch {
console.error(
"No server.key file found, please generate one using the command `deno run --allow-write --allow-read --allow-sys ./init.ts`",
);
Deno.exit(1);
}
if (!localStorage.getItem("smtpData")) {
throw new Error(
"The smtpData is missing, make sure to run the init file before running the server",
);
}
Deno.env.get("DSMTP_NAME") ?? Deno.env.set("DSMTP_NAME", "DSMTP");
Deno.env.get("DSMTP_VERSION") ?? Deno.env.set("DSMTP_VERSION", "0.0.1");
Deno.env.get("DSMTP_HOSTNAME") ?? Deno.env.set("DSMTP_HOSTNAME", "localhost");
const encoder = new TextEncoder();
const decoder = new TextDecoder();
const clients = new Map<string, ClientConn>();
const security = await new Security();
const server = new SMTPServer(security, {
hostname: Deno.env.get("DSMTP_HOSTNAME")!,
name: Deno.env.get("DSMTP_NAME")!,
});
function processCommand(
command: string,
args: string[],
server: SMTPServer,
client: ClientConn,
cmd_text?: string,
) {
const cmdState = commandMap[client.session.state];
if (cmdState) {
let cmd = cmdState[
command as unknown as keyof typeof cmdState
] as SMTPCommand;
if (!cmd) {
cmd = (cmdState as keyof typeof cmdState)["default"] as SMTPCommand;
}
if (!cmd) {
return server.sendResponse(
{ code: 502, message: "Command not implemented" },
client,
);
}
return cmd.execute(args, server, client, cmd_text);
} else {
return server.sendResponse(
{ code: 502, message: "Command " + cmdState + " not found" },
client,
);
}
}
async function handleMessages(client: ClientConn, id: string) {
for await (const res of client.conn.readable) {
try {
const data = decoder.decode(res);
let [command, ...args] = data.split(" ");
command = command.trim().toUpperCase();
console.log(`[DSMTP] Client ${client.conn.rid}: `, command, args);
await processCommand(
command,
args,
server,
client,
command,
);
//
} catch (e) {
console.error(e);
clients.delete(id);
client.conn.close();
}
}
}
async function handleConn(conn: Deno.Conn) {
let id;
try {
console.info("[DSMTP] New connection");
conn.unref();
id = (conn.remoteAddr as Deno.NetAddr).hostname.replace(/\./g, "");
clients.set(id, {
conn,
lastActivity: Date.now(),
session: {
state: SessionState.Default,
},
});
await conn.write(
encoder.encode(
`220 Welcome to ${Deno.env.get("DSMTP_NAME")} ${
Deno.env.get("DSMTP_VERSION")
} ${Deno.env.get("DSMTP_HOSTNAME")}\r\n`,
),
);
await handleMessages(clients.get(id)!, id);
clients.delete(id);
} catch {
if (id) {
clients.delete(id);
}
}
}
if (import.meta.main) {
try {
const server = Deno.listen({ port: 25, hostname: "0.0.0.0" });
console.log("[DSMTP] SMTP server started on port 25");
for await (const conn of server) {
handleConn(conn);
}
server.unref();
} catch (e) {
console.error("Error while starting the server", e);
}
}