|
|
@@ -7,295 +7,325 @@
|
|
|
|
|
|
'use strict';
|
|
|
|
|
|
-var sys_host_path = '/etc/hosts';
|
|
|
-var work_path = MacGap.homePath + '/.SwitchHosts';
|
|
|
-var data_path = work_path + '/data.json';
|
|
|
-var preference_path = work_path + '/preferences.json';
|
|
|
-var is_work_path_made;
|
|
|
-var _preferences;
|
|
|
-
|
|
|
-
|
|
|
-function mixObj(a, b) {
|
|
|
- var k;
|
|
|
- for (k in b) {
|
|
|
- if (b.hasOwnProperty(k)) {
|
|
|
- a[k] = b[k];
|
|
|
- }
|
|
|
- }
|
|
|
- return a;
|
|
|
+const fs = require('fs');
|
|
|
+const path = require('path');
|
|
|
+const request = require('request');
|
|
|
+const moment = require('moment');
|
|
|
+const notifier = require('node-notifier');
|
|
|
+const util = require('./libs/util');
|
|
|
+const platform = process.platform;
|
|
|
+
|
|
|
+const paths = require('./libs/paths');
|
|
|
+const pref = require('./libs/pref');
|
|
|
+const sys_host_path = paths.sys_host_path;
|
|
|
+const work_path = paths.work_path;
|
|
|
+const data_path = paths.data_path;
|
|
|
+const preference_path = paths.preference_path;
|
|
|
+
|
|
|
+const exec = require('child_process').exec;
|
|
|
+const stat = require('./modules/stat');
|
|
|
+stat.init();
|
|
|
+
|
|
|
+const crypto = require('crypto');
|
|
|
+function md5 (text) {
|
|
|
+ return crypto.createHash('md5').update(text).digest('hex');
|
|
|
}
|
|
|
|
|
|
-function writeFile(path, content) {
|
|
|
- MacGap.File.write(path, content, 'string');
|
|
|
-}
|
|
|
|
|
|
-function readFile(path) {
|
|
|
- return MacGap.File.read(path, 'string');
|
|
|
-}
|
|
|
+const m_lang = require('./lang');
|
|
|
+let sudo_pswd = '';
|
|
|
|
|
|
-function existPath(path) {
|
|
|
- return MacGap.File.exists(path);
|
|
|
-}
|
|
|
+function getUserLang() {
|
|
|
+ let user_lang;
|
|
|
|
|
|
-function makeBackupHosts() {
|
|
|
- return {
|
|
|
- title: 'backup',
|
|
|
- on: true,
|
|
|
- content: getSysHosts()
|
|
|
- };
|
|
|
+ user_lang = pref.get('user_language') || navigator.language || navigator.userLanguage;
|
|
|
+ if (user_lang === 'zh_CN') {
|
|
|
+ user_lang = 'cn';
|
|
|
+ } else {
|
|
|
+ user_lang = 'en';
|
|
|
+ }
|
|
|
+
|
|
|
+ return user_lang;
|
|
|
}
|
|
|
|
|
|
-function tryToCreateWorkDir() {
|
|
|
- if (existPath(work_path)) return;
|
|
|
+const lang = m_lang.getLang(getUserLang());
|
|
|
|
|
|
- var cmd = 'mkdir -p \'' + work_path + '\'';
|
|
|
- var my_task = MacGap.Task.create('/bin/sh', function (result) {
|
|
|
- if (result.status == 0) {
|
|
|
- //MacGap.File.write(sys_host_path, val, 'string');
|
|
|
- } else {
|
|
|
- alert('Fail to create work directory!\n\npath: ' + work_path);
|
|
|
- }
|
|
|
- });
|
|
|
- my_task['arguments'] = ['-c', cmd];
|
|
|
- my_task.launch();
|
|
|
-}
|
|
|
|
|
|
function getSysHosts() {
|
|
|
- var s;
|
|
|
+ let cnt = '';
|
|
|
+
|
|
|
+ try {
|
|
|
+ cnt = fs.readFileSync(sys_host_path, 'utf-8');
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e.message);
|
|
|
+ }
|
|
|
+
|
|
|
+ return cnt;
|
|
|
+}
|
|
|
+
|
|
|
+function tryToCreateWorkDir() {
|
|
|
+ if (util.isDirectory((work_path))) {
|
|
|
+ console.log('work dir exists.');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ console.log(`try to create work directory: ${work_path}`);
|
|
|
try {
|
|
|
- s = readFile(sys_host_path);
|
|
|
+ fs.mkdirSync(work_path);
|
|
|
+ console.log('work directory created.');
|
|
|
} catch (e) {
|
|
|
- alert(e.message);
|
|
|
+ alert('Fail to create work directory!');
|
|
|
}
|
|
|
- return s || '';
|
|
|
}
|
|
|
|
|
|
-function setSysHosts(val, sudo_pswd, callback) {
|
|
|
- var tmp_f = work_path + '/tmp.txt';
|
|
|
- //var cmd_f = work_path + '/cmd.sh';
|
|
|
+function saveData(content) {
|
|
|
|
|
|
- sudo_pswd = sudo_pswd || '';
|
|
|
- writeFile(tmp_f, val);
|
|
|
+ let txt = JSON.stringify({
|
|
|
+ list: content
|
|
|
+ });
|
|
|
|
|
|
- var cmd;
|
|
|
- //var cmd2;
|
|
|
+ fs.writeFile(data_path, txt, 'utf-8', (error) => {
|
|
|
+ if (error) {
|
|
|
+ alert(error.message);
|
|
|
+ }
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+function apply_UNIX(content, success) {
|
|
|
+ let tmp_fn = path.join(work_path, 'tmp.txt');
|
|
|
+ if (content) {
|
|
|
+ fs.writeFileSync(tmp_fn, content, 'utf-8');
|
|
|
+ }
|
|
|
+
|
|
|
+ let cmd;
|
|
|
if (!sudo_pswd) {
|
|
|
cmd = [
|
|
|
- 'cat "' + tmp_f + '" > ' + sys_host_path
|
|
|
- , 'rm -rf ' + tmp_f
|
|
|
+ 'cat "' + tmp_fn + '" > ' + sys_host_path
|
|
|
+ , 'rm -rf ' + tmp_fn
|
|
|
].join(' && ');
|
|
|
} else {
|
|
|
sudo_pswd = sudo_pswd.replace(/'/g, '\\x27');
|
|
|
cmd = [
|
|
|
'echo \'' + sudo_pswd + '\' | sudo -S chmod 777 ' + sys_host_path
|
|
|
- , 'cat "' + tmp_f + '" > ' + sys_host_path
|
|
|
+ , 'cat "' + tmp_fn + '" > ' + sys_host_path
|
|
|
, 'echo \'' + sudo_pswd + '\' | sudo -S chmod 644 ' + sys_host_path
|
|
|
- , 'rm -rf ' + tmp_f
|
|
|
+ // , 'rm -rf ' + tmp_fn
|
|
|
].join(' && ');
|
|
|
-
|
|
|
- //cmd2 = [
|
|
|
- // 'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- // , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- // , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- // , 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- //].join(' ; ');
|
|
|
- //cmd = cmd + ';' + cmd2;
|
|
|
- //cmd = "$'" + cmd.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
|
|
|
}
|
|
|
|
|
|
- var task = MacGap.Task.create('/bin/sh', function (result) {
|
|
|
- if (result.status == 0) {
|
|
|
- setTimeout(function () {
|
|
|
- afterSetHosts(sudo_pswd);
|
|
|
- }, 10);
|
|
|
- callback && callback();
|
|
|
- } else {
|
|
|
- //alert('An error occurred!');
|
|
|
- callback && callback(result);
|
|
|
+ exec(cmd, function(error, stdout, stderr) {
|
|
|
+ // command output is in stdout
|
|
|
+ if (error) {
|
|
|
+ if (!sudo_pswd) {
|
|
|
+ // 尝试让用户输入管理密码
|
|
|
+ SH_event.emit('sudo_prompt', (pswd) => {
|
|
|
+ sudo_pswd = pswd;
|
|
|
+ tryToApply(null, success);
|
|
|
+ });
|
|
|
+ } else {
|
|
|
+ alert(stderr);
|
|
|
+ }
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!error) {
|
|
|
+ after_apply(success);
|
|
|
}
|
|
|
});
|
|
|
- task['arguments'] = ['-c', cmd];
|
|
|
- task.launch();
|
|
|
}
|
|
|
|
|
|
-function getData(config) {
|
|
|
- if (!is_work_path_made) {
|
|
|
- tryToCreateWorkDir();
|
|
|
- is_work_path_made = true;
|
|
|
- }
|
|
|
+function _after_apply_unix(callback) {
|
|
|
+ let cmd_fn = path.join(work_path, '_restart_mDNSResponder.sh');
|
|
|
|
|
|
- var default_hosts = {
|
|
|
- title: 'My Hosts',
|
|
|
- on: false,
|
|
|
- content: '# My Hosts\n'
|
|
|
- };
|
|
|
- var default_vals = {
|
|
|
- sys: getSysHosts(),
|
|
|
- list: [default_hosts, makeBackupHosts()]
|
|
|
- };
|
|
|
- if (!existPath(data_path)) {
|
|
|
- return default_vals;
|
|
|
- }
|
|
|
+ let cmd = [
|
|
|
+ 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
+ , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
+ , 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
+ , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
+ , 'sudo killall -HUP mDNSResponder'
|
|
|
+ ].join('\n');
|
|
|
|
|
|
- var vals = {};
|
|
|
- mixObj(vals, config);
|
|
|
+ fs.writeFileSync(cmd_fn, cmd, 'utf-8');
|
|
|
|
|
|
- var s;
|
|
|
- try {
|
|
|
- s = readFile(data_path);
|
|
|
- } catch (e) {
|
|
|
- alert(e.message);
|
|
|
- return default_hosts;
|
|
|
+ exec(`/bin/sh ${cmd_fn}`, function(error, stdout, stderr) {
|
|
|
+ // command output is in stdout
|
|
|
+ if (error) {
|
|
|
+ console.log(error);
|
|
|
+ }
|
|
|
+ console.log(stdout, stderr);
|
|
|
+
|
|
|
+ callback();
|
|
|
+ });
|
|
|
+}
|
|
|
+
|
|
|
+function after_apply(callback) {
|
|
|
+
|
|
|
+ SH_event.emit('after_apply');
|
|
|
+
|
|
|
+ if (!sudo_pswd) {
|
|
|
+ callback();
|
|
|
+ return;
|
|
|
}
|
|
|
|
|
|
- try {
|
|
|
- s = JSON.parse(s);
|
|
|
- } catch (e) {
|
|
|
- alert(e.message);
|
|
|
- return default_hosts;
|
|
|
+ if (platform === 'darwin') {
|
|
|
+ _after_apply_unix(callback);
|
|
|
+ return;
|
|
|
}
|
|
|
- mixObj(vals, s);
|
|
|
|
|
|
- return vals;
|
|
|
+ callback();
|
|
|
}
|
|
|
|
|
|
-function setData(data) {
|
|
|
+function apply_Win32(content, success) {
|
|
|
+ // todo 判断写入权限
|
|
|
try {
|
|
|
- writeFile(data_path, JSON.stringify(data));
|
|
|
+ fs.writeFileSync(sys_host_path, content, 'utf-8');
|
|
|
} catch (e) {
|
|
|
- alert(e);
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-function getAllPreferences() {
|
|
|
- if (!_preferences) {
|
|
|
- var c = readFile(preference_path);
|
|
|
- try {
|
|
|
- c = JSON.parse(c);
|
|
|
- } catch (e) {
|
|
|
- c = {};
|
|
|
+ console.log(e);
|
|
|
+ let msg = e.message;
|
|
|
+ if (platform === 'win32') {
|
|
|
+ msg = `${msg}\n\n${lang.please_run_as_admin}`;
|
|
|
}
|
|
|
- _preferences = c;
|
|
|
+ alert(msg);
|
|
|
+ return;
|
|
|
}
|
|
|
+ success && success();
|
|
|
|
|
|
- return _preferences;
|
|
|
+ // todo 更新 DNS 缓存
|
|
|
}
|
|
|
|
|
|
-function getPreference(key) {
|
|
|
- var p = getAllPreferences();
|
|
|
- return p[key];
|
|
|
-}
|
|
|
|
|
|
-function setPreference(key, value) {
|
|
|
- var p = getAllPreferences();
|
|
|
- p[key] = value;
|
|
|
+function tryToApply(content, success) {
|
|
|
|
|
|
- writeFile(preference_path, JSON.stringify(p));
|
|
|
+ if (platform !== 'win32') {
|
|
|
+ apply_UNIX(content, success);
|
|
|
+ } else {
|
|
|
+ apply_Win32(content, success);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-function getURL(url, data, success, fail) {
|
|
|
- data = data || {};
|
|
|
- if (!data._r) {
|
|
|
- data._r = Math.random();
|
|
|
- }
|
|
|
|
|
|
- $.ajax({
|
|
|
- url: url,
|
|
|
- data: data,
|
|
|
- //async: false,
|
|
|
- success: function (s) {
|
|
|
- success && success(s);
|
|
|
- },
|
|
|
- error: function (e) {
|
|
|
- fail && fail(e);
|
|
|
- }
|
|
|
- });
|
|
|
-}
|
|
|
+// init
|
|
|
+tryToCreateWorkDir();
|
|
|
|
|
|
-function openURL(url) {
|
|
|
- MacGap.openURL(url);
|
|
|
-}
|
|
|
+SH_event.on('test', () => {
|
|
|
+ console.log('ttt');
|
|
|
+});
|
|
|
|
|
|
-function activate() {
|
|
|
- setTimeout(function () {
|
|
|
- MacGap.activate();
|
|
|
- }, 0);
|
|
|
-}
|
|
|
+SH_event.on('apply', (content, success) => {
|
|
|
+ success = success || function () {};
|
|
|
+ tryToApply(content, success);
|
|
|
+});
|
|
|
|
|
|
-function notify(type, title, content) {
|
|
|
- MacGap.notify({
|
|
|
- type: type,
|
|
|
- title: title,
|
|
|
- content: content
|
|
|
- });
|
|
|
-}
|
|
|
+SH_event.on('sudo_pswd', (pswd) => {
|
|
|
+ sudo_pswd = pswd;
|
|
|
+});
|
|
|
|
|
|
-function afterSetHosts(sudo_pswd, callback) {
|
|
|
- // sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
|
|
|
- // sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist
|
|
|
- // sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist
|
|
|
- // sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist
|
|
|
+SH_event.on('save_data', (content) => {
|
|
|
+ saveData(content);
|
|
|
+ ipcRenderer.send('send_host_list', content);
|
|
|
+});
|
|
|
|
|
|
- if (!sudo_pswd) {
|
|
|
- callback && callback();
|
|
|
- return;
|
|
|
+SH_event.on('check_host_refresh', (host, force=false) => {
|
|
|
+ if (host.where !== 'remote' || !host.url || !host.refresh_interval) return;
|
|
|
+
|
|
|
+ let last_refresh = host.last_refresh;
|
|
|
+ let refresh_interval = parseInt(host.refresh_interval) || 0;
|
|
|
+ if (last_refresh && !force) {
|
|
|
+ last_refresh = new Date(last_refresh);
|
|
|
+ let delta = (new Date()).getTime() - (last_refresh.getTime() || 0) / (1000 * 3600);
|
|
|
+ if (delta < refresh_interval) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- var cmd;
|
|
|
- //sudo_pswd = sudo_pswd.replace(/'/g, '\\x27');
|
|
|
- cmd = [
|
|
|
- //'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- //, 'echo \'' + sudo_pswd + '\' | sudo -S launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.mDNSResponder.plist'
|
|
|
- , 'sudo launchctl unload -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- , 'sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.discoveryd.plist'
|
|
|
- , 'sudo killall -HUP mDNSResponder'
|
|
|
- ].join('\n');
|
|
|
- //cmd = "$'" + cmd.replace(/\\/g, '\\\\').replace(/'/g, "\\'") + "'";
|
|
|
- //alert(cmd);
|
|
|
- var path = work_path + '/_restart_mDNSResponder.sh';
|
|
|
- MacGap.File.write(path, cmd, 'string');
|
|
|
-
|
|
|
- var task = MacGap.Task.create('/bin/sh', function (result) {
|
|
|
- if (result.status == 0) {
|
|
|
- callback && callback();
|
|
|
+ // refresh
|
|
|
+ console.log(`getting '${host.url}' ..`);
|
|
|
+ SH_event.emit('loading', host, true);
|
|
|
+ host.is_loading = true;
|
|
|
+ request(host.url, (err, res, body) => {
|
|
|
+ console.log(err, res.statusCode);
|
|
|
+ SH_event.emit('loading', host, false);
|
|
|
+ host.is_loading = false;
|
|
|
+ if (!err && res.statusCode === 200) {
|
|
|
+ // console.log(body);
|
|
|
+ host.content = body;
|
|
|
+ host.last_refresh = moment().format('YYYY-MM-DD HH:mm:ss');
|
|
|
+
|
|
|
+ SH_event.emit('change');
|
|
|
} else {
|
|
|
- callback && callback(result);
|
|
|
+ console.log(err, res.statusCode);
|
|
|
}
|
|
|
});
|
|
|
+});
|
|
|
|
|
|
- cmd = [
|
|
|
- '/bin/sh ' + path
|
|
|
- , 'rm -rf \'' + path + '\''
|
|
|
- ].join(';');
|
|
|
- task['arguments'] = ['-c', cmd];
|
|
|
- //task['arguments'] = [path];
|
|
|
- task.launch();
|
|
|
-}
|
|
|
+/**
|
|
|
+ * 如果本地没有 data 文件,认为是第一次运行
|
|
|
+ */
|
|
|
+function initGet() {
|
|
|
+ let dd = require('./libs/default_data');
|
|
|
+ let data = dd.make();
|
|
|
+
|
|
|
+ data.sys.content = getSysHosts();
|
|
|
+ data.list.push({
|
|
|
+ title: 'backup',
|
|
|
+ content: data.sys.content
|
|
|
+ });
|
|
|
|
|
|
-function log(msg) {
|
|
|
- /*eslint no-console: "error"*/
|
|
|
- // console.log(msg);
|
|
|
- MacGap.log(msg.toString());
|
|
|
+ return data;
|
|
|
}
|
|
|
|
|
|
module.exports = {
|
|
|
- log: log,
|
|
|
- readFile: readFile,
|
|
|
- writeFile: writeFile,
|
|
|
- existPath: existPath,
|
|
|
- getSysHosts: getSysHosts,
|
|
|
- setSysHosts: setSysHosts,
|
|
|
- getData: getData,
|
|
|
- setData: setData,
|
|
|
- getAllPreferences: getAllPreferences,
|
|
|
- getPreference: getPreference,
|
|
|
- setPreference: setPreference,
|
|
|
- getURL: getURL,
|
|
|
- openURL: openURL,
|
|
|
- activate: activate,
|
|
|
- notify: notify
|
|
|
+ md5: md5,
|
|
|
+ getHosts: function () {
|
|
|
+ let data = null;
|
|
|
+
|
|
|
+ if (!util.isFile(data_path)) {
|
|
|
+ return initGet();
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ let cnt = fs.readFileSync(data_path, 'utf-8');
|
|
|
+ data = JSON.parse(cnt);
|
|
|
+ } catch (e) {
|
|
|
+ console.log(e);
|
|
|
+ alert('bad data file.. :(');
|
|
|
+ return initGet();
|
|
|
+ }
|
|
|
+
|
|
|
+ return {
|
|
|
+ sys: {
|
|
|
+ is_sys: true
|
|
|
+ , content: getSysHosts()
|
|
|
+ },
|
|
|
+ list: data.list.map((i) => {
|
|
|
+ return {
|
|
|
+ title: i.title || ''
|
|
|
+ , content: i.content || ''
|
|
|
+ , on: !!i.on
|
|
|
+ , where: i.where || 'local'
|
|
|
+ , url: i.url || ''
|
|
|
+ , last_refresh: i.last_refresh || null
|
|
|
+ , refresh_interval: i.refresh_interval || 0
|
|
|
+ }
|
|
|
+ })
|
|
|
+ };
|
|
|
+ },
|
|
|
+ getSysHosts: function () {
|
|
|
+ return {
|
|
|
+ is_sys: true
|
|
|
+ , content: getSysHosts()
|
|
|
+ }
|
|
|
+ },
|
|
|
+ readFile: function (fn, callback) {
|
|
|
+ fs.readFile(fn, 'utf-8', callback);
|
|
|
+ },
|
|
|
+ notify: (options) => {
|
|
|
+ notifier.notify(Object.assign({
|
|
|
+ title: 'SwitchHosts!',
|
|
|
+ message: '',
|
|
|
+ icon: path.join(__dirname, 'assets', 'logo_512.png')
|
|
|
+ }, options));
|
|
|
+ },
|
|
|
+ lang: lang
|
|
|
};
|