Skip to content

Commit

Permalink
💩 SMTP Upstream
Browse files Browse the repository at this point in the history
  • Loading branch information
cfpwastaken committed Feb 23, 2024
1 parent ce7717d commit 6c63259
Show file tree
Hide file tree
Showing 3 changed files with 120 additions and 14 deletions.
17 changes: 15 additions & 2 deletions src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import getConfig from "./config.js"
// import { writeFile } from "node:fs/promises"

Check warning on line 9 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Expected line before comment

Check warning on line 9 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Expected line before comment

Check warning on line 9 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Expected line before comment

Check warning

Code scanning / ESLint

Require empty lines around comments Warning

Expected line before comment.
import POPUpstream from "./upstreams/POPUpstream.js"

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Imports should be sorted alphabetically

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

'POPUpstream' is defined but never used

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Imports should be sorted alphabetically

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

'POPUpstream' is defined but never used

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Imports should be sorted alphabetically

Check warning on line 10 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

'POPUpstream' is defined but never used

Check warning

Code scanning / ESLint

Enforce sorted import declarations within modules Warning

Imports should be sorted alphabetically.

Check warning

Code scanning / ESLint

Disallow unused variables Warning

'POPUpstream' is defined but never used.
import { readFile } from "node:fs/promises"
import SMTPClient from "./smtp/SMTPClient.js"

Check warning on line 12 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Imports should be sorted alphabetically

Check warning on line 12 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Imports should be sorted alphabetically

Check warning on line 12 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Imports should be sorted alphabetically

Check warning

Code scanning / ESLint

Enforce sorted import declarations within modules Warning

Imports should be sorted alphabetically.
// import IMAPClient from "./imap/IMAPClient.js"

Check warning

Code scanning / ESLint

Require empty lines around comments Warning

Expected line before comment.

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down Expand Up @@ -83,6 +84,18 @@ if (getConfig("pop3.enabled", true)) new POP3Server(getConfig("pop3.port", 110),
// const mails = await Promise.all(mailPromises)
// for (const mail of mails) writeFile(`mails/${mailIds[mails.indexOf(mail)]}.txt`, mail)

const up = new POPUpstream({})
// const up = new POPUpstream({})

await up.fetchNewEmails()
// await up.fetchNewEmails()

const down = new SMTPClient("smtp.ionos.de", 25, false)

await new Promise(resolve => setTimeout(resolve, 1000))

Check warning

Code scanning / ESLint

Disallow returning values from Promise executor functions Warning

Return values from promise executor functions cannot be read.
await down.ehlo("127.0.0.1")

Check warning on line 94 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (16.x)

Return values from promise executor functions cannot be read

Check warning on line 94 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (18.x)

Return values from promise executor functions cannot be read

Check warning on line 94 in src/main.ts

View workflow job for this annotation

GitHub Actions / build (20.x)

Return values from promise executor functions cannot be read
await down.startTLS()
await down.ehlo("127.0.0.1")
await down.login("*@picoscratch.de", "Passwort#0000")
await down.from("[email protected]")
await down.to("[email protected]")
await down.data(await readFile("mails/35baf742-6aab-4e39-899b-63330c40f6f9.eml", "utf-8"))
await down.quit()
107 changes: 95 additions & 12 deletions src/smtp/SMTPClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,22 +7,105 @@ const logger = new Logger("SMTPClient", "TEAL")

export default class SMTPClient {

static sendMessage(host: string, port: number, from: string, to: string, content: string, useTLS: boolean) {
// const sock = net.createConnection(port, host)
const sock = useTLS ? tls.connect(port, host) : net.createConnection(port, host)
socket: net.Socket

sock.on("data", (data: Buffer) => {
constructor(host: string, port: number, useTLS: boolean) {
this.socket = useTLS ? tls.connect(port, host) : net.createConnection(port, host)

this.socket.on("data", (data: Buffer) => {
logger.log(`Received data: ${data.toString()}`)
})
sock.on("connect", () => {
logger.log("Connected to server")
sock.write(`EHLO ${getConfig("host", "localhost")}\r\n`)
sock.write(`MAIL FROM:<${from}>\r\n`)
sock.write(`RCPT TO:<${to}>\r\n`)
sock.write("DATA\r\n")
sock.write(`${content}\r\n.\r\n`)
sock.write("QUIT\r\n")
}

writeAndWaitForResponse(data: string) {
logger.log(`Sending data: ${data}`)

return new Promise<string>(resolve => {
const { socket } = this

socket.once("data", (recv: Buffer) => {
const text = recv.toString()

logger.log(`Received data: ${text}`)

resolve(text)
})

socket.write(data)
})
}

async ehlo(host: string) {
const response = await this.writeAndWaitForResponse(`EHLO ${host}\r\n`)

return response
}

async from(from: string) {
const response = await this.writeAndWaitForResponse(`MAIL FROM:<${from}>\r\n`)

return response
}

async to(to: string) {
const response = await this.writeAndWaitForResponse(`RCPT TO:<${to}>\r\n`)

return response
}

async data(content: string) {
const response = await this.writeAndWaitForResponse("DATA\r\n")
const contentResponse = await this.writeAndWaitForResponse(`${content}\r\n.\r\n`)

return { response, contentResponse }
}

async quit() {
const response = await this.writeAndWaitForResponse("QUIT\r\n")

return response
}

async login(username: string, password: string) {
const resp = await this.writeAndWaitForResponse(`AUTH LOGIN\r\n`)
const userResp = await this.writeAndWaitForResponse(`${Buffer.from(username).toString("base64")}\r\n`)
const passResp = await this.writeAndWaitForResponse(`${Buffer.from(password).toString("base64")}\r\n`)

return { resp, userResp, passResp }
}

async startTLS() {
const response = await this.writeAndWaitForResponse("STARTTLS\r\n")

if (response.startsWith("220")) {
this.socket.removeAllListeners()
this.socket = tls.connect({ socket: this.socket })
this.socket.on("data", (data: Buffer) => {
logger.log(`Received TLS data: ${data.toString()}`)
})

return true
}

return false
}

// static sendMessage(host: string, port: number, from: string, to: string, content: string, useTLS: boolean) {
// // const sock = net.createConnection(port, host)
// const sock = useTLS ? tls.connect(port, host) : net.createConnection(port, host)

// sock.on("data", (data: Buffer) => {
// logger.log(`Received data: ${data.toString()}`)
// })
// sock.on("connect", () => {
// logger.log("Connected to server")
// sock.write(`EHLO ${getConfig("host", "localhost")}\r\n`)
// sock.write(`MAIL FROM:<${from}>\r\n`)
// sock.write(`RCPT TO:<${to}>\r\n`)
// sock.write("DATA\r\n")
// sock.write(`${content}\r\n.\r\n`)
// sock.write("QUIT\r\n")
// })
// }

}
10 changes: 10 additions & 0 deletions src/smtp/SMTPServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ export default class SMTPServer {
const email = msg.split(":")[1].split(">")[0].replace("<", "")
const [username, domain] = email.split("@")

if (info.from.endsWith(`@${getConfig("host")}`)) {
// This is an outgoing mail, we need to forward it to another server
logger.log("Mail is outgoing.")
info.to.push(email)
logger.log(`RCPT TO: ${email}`)
status(250)

return
}

if (domain != getConfig("host")) {
// The spec says we MAY forward the message ourselves,
// but simply returning 550 is fine, and the client should handle it
Expand Down

0 comments on commit 6c63259

Please sign in to comment.