1.0.2 • Published 6 years ago
lineparse v1.0.2
lineparse
This is a small line parser module for NodeJS.
API
Load the module:
const LineParse = require("lineparse");
Create an instance with a stream.Readable:
const lp = new LineParse(process.stdin);
lp.on("end", () => console.log("got EOF"));
lp.on("line", line => console.log("got line:", line));
Restart with a new input stream, e.g. after STARTTLS:
lp.restart(tlssocket);
Example
This example connects to a IMAP server, reads responses, and switches to TLS.
Environment variables IMAP_HOST, USER and PASSWORD should contain the hostname, username and password.
const LineParse = require("lineparse");
const net = require("net");
const tls = require("tls");
const crypto = require("crypto");
let socket = net.createConnection(143, process.env.IMAP_HOST);
socket.on("error", err => console.log("tcp: error:", err));
socket.on("close", () => console.log("tcp: close"));
let lp = new LineParse(socket);
lp.on("end", () => console.log("line: close"));
lp.on("line", input);
let state = 0;
function input(line)
{
console.log(">", line);
if (state == 0) {
if (!line.startsWith("* OK "))
fatal("greeting failed");
console.log("< A1 STARTTLS");
socket.write("A1 STARTTLS\r\n");
state = 1;
return;
}
if (state == 1) {
if (!line.startsWith("A1 OK "))
fatal("starttls failed");
socket = tls.connect({socket: socket, rejectUnauthorized: false});
socket.on("error", err => console.log("tls: error:", err));
socket.on("close", () => console.log("tls: close"));
socket.on("secureConnect", () => {
console.log("< A2 AUTHENTICATE CRAM-MD5");
socket.write("A2 AUTHENTICATE CRAM-MD5\r\n");
state = 3;
});
lp.restart(socket);
state = 2;
return;
}
if (state == 3) {
if (line.startsWith("+ ")) {
line = line.substring(2);
const rc = cramMD5(line);
console.log(`< ${rc}`);
socket.write(`${rc}\r\n`);
return;
}
if (!line.startsWith("A2 OK "))
fatal("authenticate failed");
console.log("< A3 SELECT INBOX");
socket.write("A3 SELECT INBOX\r\n");
state = 4;
return;
}
if (state == 4) {
if (line.startsWith("* "))
return;
if (!line.startsWith("A3 OK "))
fatal("select failed");
console.log("< A4 LOGOUT");
socket.write("A4 LOGOUT\r\n");
state = 5;
return;
}
if (state == 5) {
if (line.startsWith("* "))
return;
if (!line.startsWith("A4 OK "))
fatal("logout failed");
state = 6;
return;
}
if (state == 6)
fatal("unexpected input");
}
function fatal(...args)
{
console.error.apply(null, args);
console.error("terminating");
process.exit(1);
}
function cramMD5(challenge)
{
const hmac = crypto.createHmac("md5", process.env.PASSWORD);
hmac.update(Buffer.from(challenge, "base64").toString("utf8"));
return(Buffer.from(process.env.USER + " " + hmac.digest().toString("hex")).toString("base64"));
}