UNPKG

vpn.email.client

Version:
270 lines (268 loc) 11 kB
"use strict"; const Imap = require("imap"); const Async = require("async"); const mailcomposer = require("mailcomposer"); const mailparser = require("mailparser"); const shotrtID = require("shortid"); const idleInterval = 1000 * 60 * 60 * 24; const connectCheckTimeOut = 1000 * 15; const openBox = (imap, mailBox, cb) => { if (imap.imap.state !== 'authenticated') { return setTimeout(() => { openBox(imap, mailBox, cb); }, 1000); } imap.imap.openBox(mailBox, false, err => { if (err) { return imap.imap.addBox(mailBox, err1 => { if (err1) return cb(err); openBox(imap, mailBox, cb); }); } return cb(); }); }; /* const _sockWriteAppendData = appendData => { let val = appendData; if ( Buffer.isBuffer( appendData )) val = val.toString( 'utf8' ); this.debug && this.debug ( '=> ' + inspect(val)); this._sock.write ( val ); this._sock.write ( CRLF ); this.emit ( 'endSave' ) } */ const getImapConnect = (nodeImap, debug, forever) => { const ret = { user: nodeImap.imapUserName, host: nodeImap.imapServer, password: nodeImap.imapUserPassword, port: parseInt(nodeImap.imapPortNumber), socketTimeout: 0, authTimeout: 25000, connTimeout: 30000, tls: nodeImap.imapSsl, debug: debug ? (err) => { console.log(new Date(), ' = ', err); } : null, keepalive: { idleInterval: idleInterval } }; return ret; }; class startImap { constructor(imapAcc, listeningFolder, delBox, newMailFunction, ready, CallBack) { this.imapAcc = imapAcc; this.listeningFolder = listeningFolder; this.delBox = delBox; this.newMailFunction = newMailFunction; this.ready = ready; this.CallBack = CallBack; this.destroy = false; this.AUTHENTICATIONFAILED_error = false; this.fetching = false; this.fetchWait = false; this.busy = false; this.delayTime = null; this.idle = true; this.reserting = false; this.saveing = false; this.imap = null; this.yyy = null; this.debug = true; this.Endprocess = false; this.lastNewMailID = null; this.savefunctioncallBack = null; this.scanEmail = (imap) => { if (this.imap.state !== 'authenticated' || this.busy) { //console.log ( '=====> scanEmail cancel :', `listenFolder[${this.listeningFolder}],fetching[${this.fetching}], this.imap.state [${this.imap.state}]`) return this.fetchWait = true; } this.fetching = this.busy = true; this.fetchWait = false; const endFetch = () => { this.fetching = this.busy = false; if (this.Endprocess) { //console.log ('skip scan email Endprocess!') return this.endProcess(); } if (this.fetchWait) { //console.log ('this.fetchWait true, run scanEmail again!!!!', `listenFolder[${this.listeningFolder}]`) return this.scanEmail(imap); } }; return imap.search(['UNSEEN'], (err, results) => { if (err) { //console.log ( '=================================> imap.search error ', err ) return endFetch(); } if (!results || !results.length) { //console.log ('no results ') return endFetch(); } if (!this.lastNewMailID) { this.lastNewMailID = parseInt(results[0]) - 1; } const startId = parseInt(results[0]); const stopId = parseInt(results[results.length - 1]); if (startId !== this.lastNewMailID + 1) { results = []; for (let i = this.lastNewMailID + 1; i <= stopId; i++) { results.push(i.toString()); } } this.lastNewMailID = stopId; let fetch = imap.fetch(results, { markSeen: true, bodies: '' }); fetch.on('message', (msg, seqno) => { let mp = new mailparser.MailParser(); mp.once('end', (msg) => { if (msg.attachments && msg.attachments.length) { const buffer = msg.attachments[0].content; this.debug ? console.log(`new mail {${buffer.length}}`) : null; return this.newMailFunction(buffer); } this.debug ? console.log('get new mail but have not attachments') : null; }); msg.on('body', (data) => { return data.pipe(mp); }); }); fetch.once('error', err1 => { console.log('=================================> imap.search fetch error:', err1); return endFetch(); }); fetch.once('end', () => { return Async.series([ next => imap.addFlags(results, ['\\Deleted'], next), next => imap.expunge(results, next) ], () => { fetch.removeAllListeners(); fetch = null; this.busy = false; this.debug ? console.log(`new mail: [${results}]`) : null; if (this.fetchWait) return this.scanEmail(imap); return endFetch(); }); }); }); }; this.save = (enCtypeMessage, writeFolder, CallBack) => { this.saveing = this.busy = true; if (CallBack && typeof CallBack === 'function') this.savefunctioncallBack = CallBack; const socket = this.imap._sock; const email = mailcomposer({ date: ' ', messageId: shotrtID.generate(), attachments: [{ filename: false, content: enCtypeMessage }] }); let TimeOut = null; return Async.waterfall([ next => email.build(next), (_data, next) => { this.debug ? console.time(`append{${enCtypeMessage.length}}`) : null; TimeOut = setTimeout(() => { this.debug ? console.log(`[${enCtypeMessage.length}]`, '============================> save time out') : null; this.imap.end(); }, enCtypeMessage.length < 65536 ? 5000 : 15000); this.imap.append(_data, { mailbox: writeFolder }, next); } ], (err, no) => { clearTimeout(TimeOut); this.saveing = this.busy = false; this.savefunctioncallBack = null; this.debug ? console.timeEnd(`append{${enCtypeMessage.length}}`) : null; this.debug ? console.log(`[${no}]`) : null; return CallBack(err); }); }; this.yyy = getImapConnect(imapAcc, false, true); this.connectImap(); } noopProcess() { if (this.Endprocess || this.idle || !this.listeningFolder) return this.debug ? console.log(`[${this.Endprocess}][${this.idle}][${this.listeningFolder.length}]`) : null; setTimeout(() => { if (this.imap.state !== 'authenticated' || !this.imap._queue || this.busy || this.imap._queue.length) return this.noopProcess(); this.imap._enqueue('NOOP', true); return this.noopProcess(); }, 10 + Math.random() * 90); } idleSupport() { this.idle = this.imap.serverSupports('IDLE'); if (!this.idle) { this.debug ? console.log('idle not Support') : null; return this.noopProcess(); } } checkBusy() { if (!this.imap || this.imap.state !== 'authenticated' || !this.imap._sock.writable || !this.imap._sock.readable) { this.endProcess(); return false; } if (this.imap._queue) { if (this.imap._queue.length) return false; return this.busy = true; } } endProcess() { if (this.Endprocess) return; //console.log ('imap endProcess ', this.listeningFolder) this.Endprocess = true; this.ready(false); this.imap.end(); if (this.savefunctioncallBack) { this.savefunctioncallBack(new Error('socket end')); } if (this.CallBack && typeof this.CallBack === 'function') return this.CallBack(this.AUTHENTICATIONFAILED_error ? new Error('AUTHENTICATIONFAILED') : null); return process.exit(this.AUTHENTICATIONFAILED_error ? 1 : 0); } connectImap() { this.ready(false); this.imap = new Imap(this.yyy); this.destroy = this.fetching = this.AUTHENTICATIONFAILED_error = this.fetchWait = this.busy = false; this.imap.once('ready', () => { this.reserting = false; // this.imap._sockWriteAppendData = _sockWriteAppendData if (this.listeningFolder && this.listeningFolder.length) { return openBox(this, this.listeningFolder, err => { clearTimeout(this.delayTime); if (err) { this.debug ? console.log('imap openBox error!') : null; return this.imap.end(); } this.imap.on('mail', mail => { return this.scanEmail(this.imap); }); this.idleSupport(); this.ready(true); }); } this.ready(true); }); this.imap.once('error', (err) => { this.debug ? console.log('this.imap.on ERROR', `listen:[${this.listeningFolder}]`, err.message) : null; this.AUTHENTICATIONFAILED_error = /AUTHENTICATIONFAILED/.test(err.textCode); this.imap.end(); this.endProcess(); }); this.imap.once('end', () => { //console.log ( 'this.imap.on END',`listen:[${this.listeningFolder}], saveing:[${this.saveing}], busy:[${this.busy}]`) this.endProcess(); }); return this.imap.connect(); } } Object.defineProperty(exports, "__esModule", { value: true }); exports.default = startImap;