UNPKG

vpn.email

Version:
347 lines (278 loc) 12 kB
import * as child_process from 'child_process'; import Imap from './imap'; import * as mailparser from 'mailparser'; import * as Mailcomposer from 'mailcomposer'; import * as express from 'express' import * as util from './util' import * as randomStr from './randomString'; import * as Async from 'async' import * as fs from 'fs' import * as shortID from 'shortid' import * as Compress from './compress' import ImapV1 from './ImapV1' let lastMessage = '' let lastErr = '' const echoTimeOut = 1000 * 10; const child_processExec = ( command: string, SubEnv: any, power: string, debug: boolean, CallBack: ICallBack ) => { const comm = ' node ' + command; const envSt = JSON.stringify ( global.process.env ); const SubEnvSt = JSON.stringify ( SubEnv ); const env = JSON.parse ( '{' + envSt.substr ( 1, envSt.length - 2 ) + ',' + SubEnvSt.substr ( 1, SubEnvSt.length - 2 ) + '}' ); //console.log (util.inspect ( env )) const grep = child_process.exec ( comm, { env: env }); grep.stdout.on ( 'data', ( data: Buffer ) => { lastMessage = data.toString ( 'utf8' ) if ( debug ) console.log ( 'grep.stdout == ', lastMessage ); }) grep.stderr.on ( 'data', ( data: Buffer ) => { lastErr = data.toString ( 'utf8' ); if ( debug ) console.log ('grep.stderr ==> ', lastErr ) }) grep.on ( 'close', ( code ) => { if ( debug ) console.log ( 'exit with code:', code ); CallBack ( null, code ) }) } const callback = () => { if ( /Invalid credentials \(Failure\)|Lookup failed|[Incorrect|Invalid] username or password|Authentication failed|authentication failed|LOGIN|Login|service\.mail\.qq\.com/.test ( lastMessage )) return ( new Error ( 'passwordErrMsg' )); if ( /getaddrinfo ENOTFOUND/.test ( lastMessage )) return ( new Error ( 'hostAddrErr' )) if ( /Your account is not enabled for IMAP use|LOGIN auth error/.test ( lastMessage )) return ( new Error ( 'imapEnable' )); if ( /Timed out while authenticating with server/.test ( lastMessage )) return ( new Error ( 'hostAuthTimeOut' )) if ( /Timed out while connecting to server/.test ( lastMessage )) return ( new Error ( 'hostPortTimeOut' )); if ( /Hostname\/IP doesn't match certificate's altnames/.test ( lastMessage )) return ( new Error ('hostCertError' )) if ( /ECONNREFUSED/.test ( lastMessage )) return ( new Error ( 'ECONNREFUSED' )) return ( new Error ( 'UnknowErr' )) } export const testImapCommand = ( email, password, host: string, tls: boolean, port: number , CallBack ) => { const env: ImapClassEnv = { email: email, app_password: password, host: host, tls: tls, port: port, uuid: '' } child_processExec ( 'views/api/_testImapAccount', env, '0', true, ( err, data ) => { if ( err || data !== 0 ) { return CallBack ( callback ()) } CallBack () }) } export class connectImap { static systemEmailAddress = 'test@vpn.email' private timeoutFun; private imap: Imap; private mailFolder: string; private emailAddress: string; private echoTimeOut = null; private dockerPublicKey = null; private echoBack = null; public saveToVpnEmail ( DockerFolder: string, data: any, CallBack ) { Async.waterfall ([ next => util.Encrypt ( JSON.stringify ( data ), this.sockSession.dockerPublicKey, this.sockSession.keyPair.privateKeyUTF8, this.sockSession.keyPairPassword, next ), ( data, next ) => { const emailOption: mailcomposerOption = { from: this.emailAddress, to: [ connectImap.systemEmailAddress ], attachments:[{ content: data }] } const email = Mailcomposer ( emailOption ); this.imap.save ( DockerFolder, email, next ) } ], err => { if ( err ) { console.log ('saveToVpnEmail Async.waterfall have ERROR', err ) return CallBack ( 'UnknowErr' ) } CallBack () }) } processMailBody ( body: string, email: mailparser.ParsedMail ) { try { const datas = JSON.parse ( body ); const vpnServerConnectData: ISentMessage = datas; // echo come if ( vpnServerConnectData.echo ) { clearTimeout ( this.echoTimeOut ) console.log ('echo return') if (this.echoBack && typeof this.echoBack === 'function' ) this.echoBack () this.echoBack = null; this.echoTimeOut = null; return; } const command : IClientEmailCommand = datas; if ( command.command && command.command.length ) { if ( /^activePassword$/.test ( command.command )) this.sockSession.vpnServerConnectData.active = true; if ( /^echo$/.test ( command.command )) { } if ( /^disconnect$/.test ( command.command )) { this.sockSession.vpnEmailServerKeepConnected = false; } } // have docker public key if ( vpnServerConnectData.haveDockerPublicKey && email.attachments[1].content && email.attachments[1].content.length ) { const key = email.attachments[1].content.toString('utf8') if ( /^-----BEGIN PGP PUBLIC KEY BLOCK-----\n/.test( key )) { this.dockerPublicKey = key; this.sockSession.dockerPublicKey = key; } this.sockSession.vpnServerConnectData = datas; } return datas } catch ( ex ) { console.log ( 'Server data format ERROR', body ) } } constructor ( private sockSession: ISockSession, CallBack: ( data ) => void ) { //this.timeoutFun = setTimeout( ioEvent, 1000 * 120, Io); const Config: IinputData = sockSession.imapArray; const key = sockSession.keyPair this.mailFolder = Config.uuid; this.emailAddress = Config.account; this.imap = new Imap ( Config.imapUserName, Config.imapUserPassword, Config.imapServer, parseInt ( Config.imapPortNumber ), Config.imapSsl, true, Config.uuid, true, true, false, false ) this.imap._events.on ( 'email', ( email: mailparser.ParsedMail ) => { console.log ( 'listening folder have data from vpn.email server!' ) sockSession.imapConnected = true; if ( /test@vpn.email/i.test ( email.from [0].address )) { if ( email.attachments && email.attachments.length ) { const text = email.attachments[0].content.toString ( 'utf8' ) if ( /^-----BEGIN PGP MESSAGE-----/.test ( text )) { util.Decryption ( text, sockSession.keyPairPassword, key.privateKeyUTF8, sockSession.dockerPublicKey || sockSession.vpnEmailPublicKey, ( err, data ) => { if ( err ) return console.log ( 'Decryption mail err', text ) const datas = this.processMailBody ( data.data, email ) CallBack ( data.data ) }) return } } } console.log ( 'unknow data format!', email.subject ) }) this.imap._events.on ( 'imapReady', () => { console.log ( 'IMAP connected' ) sockSession.imapConnected = true; }) } public destroy () { this.imap.distroy (); } public echoRespon ( CallBack: ICallBack ) { if ( ! this.sockSession.vpnServerConnectData || ! this.sockSession.vpnServerConnectData.serverMailFolder ) return ( new Error ( '' )) this.echoBack = CallBack const emailOption: mailcomposerOption = { subject: 'echo', from: this.emailAddress, to: [ connectImap.systemEmailAddress ] } const email = Mailcomposer ( emailOption ); console.log ( 'save echo to server folder:', this.sockSession.vpnServerConnectData.serverMailFolder ) this.imap.save ( this.sockSession.vpnServerConnectData.serverMailFolder, email, () => { console.log ( 'sent echo success!' ) }) this.echoTimeOut = setTimeout(() => { CallBack ( new Error ( 'no' )) this.echoBack = null; }, echoTimeOut ) } } interface connectOption { email: string; password: string; host: string; tls: boolean; port: number; } const _ImapPairTest = ( user: IinputData, CallBack ) => { const listeningFolder = shortID.generate() const uuid = shortID.generate() const pass = shortID.generate() const testText = new Buffer (1200).toString('base64') let startTime: Date = null let timeOut = null let callBackData = 0 let imapErr = null const content = Compress.packetBuffer ( 0, 0, uuid, Compress.encrypt ( testText, pass )) const newMail = ( mail: Buffer ) => { const endTime = new Date () try { const pp = Compress.openPacket ( mail ) if ( pp.uuid != uuid ) return Compress.decrypt ( pp.buffer, pass, ( err, data ) => { imap.destroyImap () clearTimeout ( timeOut ) if ( err ) return imapErr = err return callBackData = new Date().getTime() - startTime.getTime () }) } catch ( ex ) { console.log ('ImapPairTest new email got ex', ex ) } } const imap = new ImapV1 ( user, listeningFolder, false, true, newMail, ( err, connectDelay ) => { if ( err ) return CallBack ( err ) CallBack( imapErr, callBackData ) }) imap.save ( content, listeningFolder, err => { if ( err ) { console.log ('save got err!', err ) imap.destroyImap () return imapErr = err } startTime = new Date () timeOut = setTimeout (() => { console.log ('timeout fire, destroy imap') imap.destroyImap () return imapErr = new Error ('timeout') }, 15000 ) }) } const _doImapPairTest = ( user: IinputData, maxConnect: number, CallBack ) => { const loop = [] let errCount = 0 console.log ( 'start test imap account:', user.imapUserName ) for ( let i = 0; i < maxConnect; i ++ ) { loop.push ( next => _ImapPairTest ( user, next )) } Async.parallel ( loop, ( err, time: number[] ) => { console.log ('_doImapPairTest callack!', err, time) if ( err ) return CallBack ( err ) if ( time.every ( n => n === -1 )) return CallBack ( new Error ('data error')) let u = 0 time.map ( n => u += n ) return CallBack ( null, u /maxConnect ) }) } export const doImapPairTest = ( user: IinputData, connectNumber: number, loopCount: number, CallBack ) => { const loop = [] for ( let i = 0; i < loopCount; i ++ ) { loop.push ( next => _doImapPairTest ( user, connectNumber, next )) } Async.series ( loop, ( err, n: number[] ) => { console.log ('doImapPairTest callback', err, n) if ( err ) return CallBack ( err ) let u = 0 n.map ( n => u += n ) return CallBack ( null, u /loopCount ) }) }