/** * 虚拟终端模块 * 更新:2016/04/13 * 作者:蚁逅 */ const LANG = antSword['language']['terminal']; const LANG_T = antSword['language']['toastr']; class Terminal { constructor(opts) { // 生存一个随机ID,用于标识多个窗口dom const hash = String(Math.random()).substr(2, 10); // 初始化UI::tabbar const tabbar = antSword['tabbar']; tabbar.addTab( `tab_terminal_${hash}`, ` ${opts['ip']}`, null, null, true, true ); tabbar.attachEvent('onTabClick', (id,lid) => { if (id !== `tab_terminal_${hash}`) { return }; this.term ? this.term.focus() : 0; }); // 初始化UI::cell const cell = tabbar.cells(`tab_terminal_${hash}`); cell.attachHTMLString(`
`); this.path = ''; this.opts = opts; this.hash = hash; this.term = null; this.cell = cell; this.isWin = true; this.core = new antSword['core'][opts['type']](opts); this.cache = new antSword['CacheManager'](this.opts['_id']); this .getInformation() .then((ret) => { this.initTerminal(ret['info'], ret['dom']); }) .catch((err) => { toastr.error((typeof(err) === 'object') ? JSON.stringify(err) : String(err), LANG_T['error']); this.cell.progressOff(); this.cell.close(); }) } /** * 获取目标信息 * @return {Promise} 返回一个Promise操作对象//{dom,info} */ getInformation() { return new Promise((ret, rej) => { // 获取DOM const dom = $(`#div_terminal_${this.hash}`); // 获取缓存 let infoCache = this.cache.get('info'); // 如果有缓存?初始化终端:获取信息&&保存缓存&&初始化终端 if (infoCache) { return ret({ dom: dom, info: infoCache }); } // 开始获取信息 this.cell.progressOn(); this.core.request( this.core.base.info() ).then((_ret) => { this.cell.progressOff(); this.cache.set('info', _ret['text']); return ret({ dom: dom, info: _ret['text'] }); }).catch((e) => { rej(e); }); }); } /** * 初始化终端 * @param {String} ret 目标信息 * @param {Object} dom 要操作的dom元素 * @return {None} [description] */ initTerminal(ret, dom) { let info = ret.split('\t'); let infoUser, infoPath, infoDrive, infoSystem; let banner = `[[b;cyan;](*) ${LANG['banner']['title']}]`; // 判断数据是否正确 if (info.length === 4) { infoPath = info[0]; infoDrive = info[1]; infoSystem = info[2]; infoUser = info[3]; } else if (info.length === 2) { infoPath = info[0]; infoDrive = info[1]; } else { toastr.error('Loading infomations failed!
' + ret, LANG_T['error']); this.cache.del('info'); return this.cell.close(); } // 转换路径特殊字符 infoPath = infoPath.replace(/\\/g, '/').replace(/\.$/, ''); // 判断是否为!win this.isWin = !(infoPath.substr(0, 1) === '/') this.path = infoPath; // 组合banner banner += `\n[[b;#99A50D;]${LANG['banner']['path']}]: [[;#C3C3C3;]${infoPath}]`; banner += `\n[[b;#99A50D;]${LANG['banner']['drive']}]: [[;#C3C3C3;]${infoDrive}]`; if (info.length === 4) { banner += `\n[[b;#99A50D;]${LANG['banner']['system']}]: [[;#C3C3C3;]${infoSystem}]`; banner += `\n[[b;#99A50D;]${LANG['banner']['user']}]: [[;#C3C3C3;]${infoUser}]`; } // 初始化终端 this.term = dom.terminal( (cmd, term) => { if (!cmd) { return false } // 如果为exit||quit则关闭窗口 if (cmd === 'exit' || cmd === 'quit') { return this.cell.close() } // clear清空 if (cmd === 'cls' || cmd === 'clear') { return term.clear() } term.pause(); // 是否有缓存 let cacheTag = 'command-' + new Buffer(this.path + cmd).toString('base64'); let cacheCmd = this.cache.get(cacheTag); if ( (this.opts.otherConf || {})['terminal-cache'] === 1 && cacheCmd ) { term.echo( antSword.noxss(cacheCmd, false) ); return term.resume(); }; // 获取自定义执行路径 let _bin = this.isWin ? 'cmd' : '/bin/sh'; let _confBin = (this.opts['otherConf'] || {})['command-path']; _bin = _confBin || _bin; // 开始执行命令 this.core.request( this.core.command.exec({ cmd: this.parseCmd(cmd, this.path), bin: _bin }) ).then((ret) => { let _ = ret['text']; // 解析出命令执行路径 const indexS = _.lastIndexOf('[S]'); const indexE = _.lastIndexOf('[E]'); let _path = _.substr(indexS + 3, indexE - indexS - 3); let output = _.replace(`[S]${_path}[E]`, ''); _path = _path.replace(/\n/g, '').replace(/\r/g, ''); this.path = _path || this.path; term.set_prompt(this.parsePrompt(infoUser)); // 去除换行符 [ /\n\n$/, /^\n\n/, /\r\r$/, /^\r\r/, /\r\n$/, /^\r\n/, /\n\r$/, /^\n\r/, /\r$/, /^\r/, /\n$/, /^\n/ ].map((_) => { output = output.replace(_, ''); }); if (output.length > 0) { term.echo( antSword.noxss(output, false) ); // 保存最大100kb数据 if (output.length < (1024 * 1024)) { this.cache.set(cacheTag, output); }; }; term.resume(); }).catch((_) => { // term.error('ERR: ' + (_ instanceof Object) ? JSON.stringify(_) : String(_)); term.resume(); }); }, { greetings: banner, name: `terminal_${this.hash}`, prompt: this.parsePrompt(infoUser), numChars: 2048, exit: false, // < 1.0.0 时使用3个参数 completion: (term, value, callback) => {} completion: (value, callback) => { callback( this.isWin ? [ 'dir', 'whoami', 'net', 'ipconfig', 'netstat', 'cls', 'wscript', 'nslookup', 'copy', 'del', 'ren', 'md', 'type', 'ping' ] : [ 'cd', 'ls', 'find', 'cp', 'mv', 'rm', 'ps', 'kill', 'file', 'tar', 'cat', 'chown', 'chmod', 'pwd', 'history', 'whoami', 'ifconfig', 'clear', 'ping' ] ) } }); } /** * 根据执行命令&&路径生成最终command * @param {String} cmd 要执行的命令 * @param {String} path 当前路径 * @return {String} 最终执行命令 */ parseCmd(cmd, path) { path = path.replace(/\\\\/g, '\\').replace(/"/g, '\\"').replace(/\\/g, '\\\\'); return (this.isWin ? `cd /d "${path}"&${cmd}&echo [S]&cd&echo [E]` : `cd "${path}";${cmd};echo [S];pwd;echo [E]` ); } /** * 生成路径提示符 * @param {String} user 当前用户名 * @return {String} term输出字符串 */ parsePrompt(user) { return antSword.noxss(this.isWin ? `[[b;white;]${this.path.replace(/\//g, '\\')}> ]` : (user ? `([[b;#E80000;]${user}]:[[;#0F93D2;]` : '[[;0F93D2;]') + this.path + ']) $ ' ); } } // export default Terminal; module.exports = Terminal;