vpn.email
Version:
vpn.email client
276 lines (216 loc) • 6.64 kB
text/typescript
import * as Imap from "imap";
import * as Async from "async";
import * as mailcomposer from "mailcomposer";
import * as mailparser from 'mailparser';
import * as shortID from 'shortid'
import * as Net from 'net'
import * as Stream from 'stream'
const idleInterval = 1000 * 60 * 60 * 24
const openBox = ( imap: startImap, mailBox, cb: ( err?: Error ) => void ) => {
if ( imap.imap.state !== 'authenticated' ) {
console.log (imap.imap.state)
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 getImapConnect = ( nodeImap: IinputData, debug: boolean, forever: boolean ) => {
const keepalive = ( /outlook.com$|/.test (nodeImap.imapServer))
const ret : IMAP.Config = {
user: nodeImap.imapUserName,
host: nodeImap.imapServer,
password: nodeImap.imapUserPassword,
port: parseInt ( nodeImap.imapPortNumber ),
tls: nodeImap.imapSsl,
debug: debug
? ( err ) => { console.log ( new Date(), ' = ', err )}
: null,
keepalive: {
interval: 400,
idleInterval: idleInterval
,forceNoop: true
}
}
return ret;
}
export default class startImap {
private lastNewMail = new Date()
private checkBusy = null
private destroy = false;
private reConnectCount = 0
private connected = false
private fetching = false
private fetchWait = false
private busy = false
private delayTime = null
private scanEmail = ( imap: IMAP.Connection ) => {
this.fetching = true
this.fetchWait = false
return imap.search ( ['UNSEEN'], ( err, results ) => {
if ( err ) {
console.log ( 'imap.search error ')
if ( this.fetchWait )
return this.scanEmail ( imap )
return this.fetching = false
}
if ( !results || ! results.length ) {
if ( this.fetchWait )
return this.scanEmail ( imap )
return this.fetching = false
}
let fetch = imap.fetch ( results, { markSeen : true, bodies: ''});
fetch.on ( 'message', msg => {
let mp = new mailparser.MailParser();
mp.once ( 'end', ( msg: mailparser.ParsedMail ) => {
if ( msg.attachments && msg.attachments.length ) {
return this.newMailFunction ( msg.attachments [0].content )
}
/*
const interval = 100 + Math.random () * 200
this.imap._config.keepalive.interval = interval
console.log ( 'have not attachments' )
*/
})
msg.on ( 'body', ( data: Stream.Readable ) => {
return data.pipe (mp)
})
msg.once ( 'end', () => {
})
})
fetch.once ( 'error', err => {
this.fetching = this.busy = false
fetch.removeAllListeners ()
fetch = null
return console.log ( this.imapAcc.imapUserName,' fetch error ', err )
})
fetch.once ( 'end', () => {
return Async.series ([
next => imap.addFlags ( results, ['\\Deleted'], next ),
next => imap.expunge ( results, next )
], err => {
fetch.removeAllListeners ()
fetch = null
//console.log ( 'fetch.once END!!' )
if ( this.fetchWait )
return this.scanEmail ( imap )
return this.busy = this.fetching = this.fetchWait = false
/*
this.lastNewMail = new Date ()
const interval = 100 + Math.random () * 200
this.imap._config.keepalive.interval = interval
//console.log ( ' fetch end set new interval = ', interval )
*/
})
})
})
}
public imap: IMAP.Connection = null;
public save = ( enCtypeMessage: Buffer , writeFolder: string, CallBack: ICallBack ) => {
if ( this.destroy )
return
if ( !this.connected ) {
return setTimeout (() => {
this.save ( enCtypeMessage, writeFolder, CallBack );
}, 1000 );
}
this.busy = true
const email = mailcomposer (
{
attachments: [{
filename: false,
content: enCtypeMessage
}]
}
)
return Async.waterfall ([
next => email.build ( next ),
( data: Buffer, next ) => {
//console.log (`${ new Date().toISOString() }===========>[${jj.uuid}][${jj.serial}]->[${jj.command}][${jj.buffer.toString('hex',0,30)}]`)
this.imap.append ( data, { mailbox: writeFolder }, next )
}
], err => {
this.busy = false
if ( err ) {
console.log ('imap save got error!', err.message )
return CallBack ( err )
}
return CallBack ()
})
}
public destroyImap () {
if ( this.busy ) {
console.log ('this.busy destroy wait 5 seconds!', this.listeningFolder)
return setTimeout(() => {
this.destroyImap ()
}, 5000 );
}
this.destroy = true
if ( !this.delBox ) {
return this.imap.end ()
}
this.imap.delBox ( this.listeningFolder, () => {
return this.imap.end ()
})
}
private connectImap ( yyy: IMAP.Config ) {
this.imap = new Imap ( yyy );
this.imap.once ( 'ready', err => {
return openBox ( this, this.listeningFolder, err => {
if ( err ) {
this.destroyImap()
}
this.delayTime = new Date ().getTime () - this.delayTime
this.connected = true
})
})
this.imap.on ( 'mail', mail => {
this.busy = true
if ( this.fetching )
return this.fetchWait = true
return this.scanEmail ( this.imap )
})
this.imap.on ( 'error', ( err: Error ) => {
console.log ('err!', err.message)
if ( this.reConnect || /^Timed out while authenticating with server$|ECONNRESET/.test( err.message ))
return setTimeout (() => {
if ( this.imap && this.imap.connect ) {
console.log ('try re connect')
return this.imap.connect ()
}
console.log ('dont try again')
this.destroy = true
return this.destroyImap ()
}, 3000 )
console.log ( 'this.imap.on error, stop imap')
this.destroy = true
this.imap.removeAllListeners()
this.imap.destroy ()
return this.CallBack ( err )
});
this.imap.once ( 'end', () => {
if ( this.reConnect && ! this.destroy ) {
return this.connectImap ( yyy )
}
this.imap.removeAllListeners ()
this.imap = null
return this.CallBack ( null, this.delayTime )
});
this.delayTime = new Date().getTime()
return this.imap.connect ();
}
constructor ( private imapAcc: IinputData, private listeningFolder: string, private reConnect: boolean, private delBox: boolean,
private newMailFunction: ( msg: Buffer ) => void, private CallBack: ICallBack ) {
const yyy = getImapConnect ( imapAcc, false, true );
this.connectImap ( yyy )
}
}