@push.rocks/smartimap
Version:
A Node.js library for event-driven streaming and parsing of IMAP email messages.
126 lines • 10.9 kB
JavaScript
import * as net from "net";
export class ImapServer {
constructor() {
this.users = new Map();
this.server = net.createServer(this.handleConnection.bind(this));
}
// Add a user for authentication
addUser(username, password) {
if (this.users.has(username)) {
throw new Error(`User "${username}" already exists.`);
}
this.users.set(username, { username, password, inboxes: new Map() });
}
// Add an inbox for a user
createInbox(username, inboxName) {
const user = this.users.get(username);
if (!user) {
throw new Error(`User "${username}" does not exist.`);
}
if (user.inboxes.has(inboxName)) {
throw new Error(`Inbox "${inboxName}" already exists for user "${username}".`);
}
user.inboxes.set(inboxName, { name: inboxName, messages: [] });
}
// Start the server
start(port) {
this.server.listen(port, () => {
console.log(`IMAP Server started on port ${port}`);
});
}
// Stop the server
stop() {
this.server.close(() => {
console.log("IMAP Server stopped.");
});
}
// Handle a new client connection
handleConnection(socket) {
let currentUser = null;
let selectedInbox = null;
socket.write("* OK IMAP4rev1 Service Ready\r\n");
socket.on("data", (data) => {
const command = data.toString().trim();
console.log(`Received command: ${command}`);
const [tag, keyword, ...args] = command.split(" ");
let response = "";
try {
switch (keyword.toUpperCase()) {
case "LOGIN": {
const [username, password] = args;
const user = this.users.get(username);
if (user && user.password === password) {
currentUser = user;
response = `${tag} OK LOGIN completed`;
}
else {
response = `${tag} NO LOGIN failed`;
}
break;
}
case "LIST": {
if (!currentUser) {
response = `${tag} NO Not authenticated`;
break;
}
const inboxNames = Array.from(currentUser.inboxes.keys()).map((inbox) => `* LIST () "/" ${inbox}`);
response = `${inboxNames.join("\r\n")}\r\n${tag} OK LIST completed`;
break;
}
case "SELECT": {
if (!currentUser) {
response = `${tag} NO Not authenticated`;
break;
}
const inboxName = args[0];
const inbox = currentUser.inboxes.get(inboxName);
if (inbox) {
selectedInbox = inbox;
response = `* FLAGS (\\Answered \\Flagged \\Deleted \\Seen \\Draft)\r\n* EXISTS ${inbox.messages.length}\r\n${tag} OK [READ-WRITE] SELECT completed`;
}
else {
response = `${tag} NO SELECT failed: No such mailbox`;
}
break;
}
case "FETCH": {
if (!selectedInbox) {
response = `${tag} NO No mailbox selected`;
break;
}
const [id] = args;
const message = selectedInbox.messages.find((msg) => msg.id === id);
if (message) {
response = `* ${id} FETCH (BODY[TEXT] {${message.body.length}}\r\n${message.body}\r\n)\r\n${tag} OK FETCH completed`;
}
else {
response = `${tag} NO FETCH failed: No such message`;
}
break;
}
case "LOGOUT": {
response = `* BYE IMAP4rev1 Server logging out\r\n${tag} OK LOGOUT completed`;
socket.write(response + "\r\n");
socket.end();
return;
}
default: {
response = `${tag} BAD Unknown command`;
break;
}
}
}
catch (error) {
response = `${tag} BAD Error: ${error.message}`;
}
socket.write(response + "\r\n");
});
socket.on("close", () => {
console.log("Client disconnected.");
});
socket.on("error", (err) => {
console.error("Socket error:", err);
});
}
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY2xhc3Nlcy5pbWFwc2VydmVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vdHMvY2xhc3Nlcy5pbWFwc2VydmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sS0FBSyxHQUFHLE1BQU0sS0FBSyxDQUFDO0FBc0IzQixNQUFNLE9BQU8sVUFBVTtJQUlyQjtRQUNFLElBQUksQ0FBQyxLQUFLLEdBQUcsSUFBSSxHQUFHLEVBQUUsQ0FBQztRQUN2QixJQUFJLENBQUMsTUFBTSxHQUFHLEdBQUcsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLGdCQUFnQixDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsQ0FBQyxDQUFDO0lBQ25FLENBQUM7SUFFRCxnQ0FBZ0M7SUFDekIsT0FBTyxDQUFDLFFBQWdCLEVBQUUsUUFBZ0I7UUFDL0MsSUFBSSxJQUFJLENBQUMsS0FBSyxDQUFDLEdBQUcsQ0FBQyxRQUFRLENBQUMsRUFBRSxDQUFDO1lBQzdCLE1BQU0sSUFBSSxLQUFLLENBQUMsU0FBUyxRQUFRLG1CQUFtQixDQUFDLENBQUM7UUFDeEQsQ0FBQztRQUNELElBQUksQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLFFBQVEsRUFBRSxFQUFFLFFBQVEsRUFBRSxRQUFRLEVBQUUsT0FBTyxFQUFFLElBQUksR0FBRyxFQUFFLEVBQUUsQ0FBQyxDQUFDO0lBQ3ZFLENBQUM7SUFFRCwwQkFBMEI7SUFDbkIsV0FBVyxDQUFDLFFBQWdCLEVBQUUsU0FBaUI7UUFDcEQsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7UUFDdEMsSUFBSSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQ1YsTUFBTSxJQUFJLEtBQUssQ0FBQyxTQUFTLFFBQVEsbUJBQW1CLENBQUMsQ0FBQztRQUN4RCxDQUFDO1FBQ0QsSUFBSSxJQUFJLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxTQUFTLENBQUMsRUFBRSxDQUFDO1lB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