├── .gitignore ├── .gitmodules ├── CHANGES ├── CONTRIBUTING.md ├── Cakefile ├── LICENSE ├── README.md ├── args.js ├── bootstrap.min.js ├── css └── bootstrap.min.css ├── icon.png ├── img ├── glyphicons-halflings-white.png └── glyphicons-halflings.png ├── index.html ├── jquery-1.9.0.min.js ├── main.js ├── menu_icon.png ├── menu_icon@2x.png ├── package.json ├── screenshot.png ├── src ├── args.coffee ├── main.coffee └── update.coffee ├── update.js └── utils ├── Info.plist ├── build.sh ├── make_icon.sh └── shadowsocks.icns /.gitignore: -------------------------------------------------------------------------------- 1 | dist 2 | node-webkit.app/ 3 | node_modules/ 4 | nwsnapshot 5 | gui-config.json 6 | *.exe 7 | *.dll 8 | *.pak 9 | .idea 10 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Lupino/shadowsocks-gui/8bc29e0d009d22194e438e4a14edc63c723447f1/.gitmodules -------------------------------------------------------------------------------- /CHANGES: -------------------------------------------------------------------------------- 1 | 0.4.1 2014-03-28 2 | - Close button should hide the window instead of quit on OS X 3 | - Listen to 127.0.0.1 instead of 0.0.0.0 4 | 5 | 0.4 2013-07-31 6 | - Update shadowsocks-nodejs to 1.4.1 7 | 8 | 0.3.0 2013-07-21 9 | - Update shadowsocks-nodejs to 1.4.0 10 | 11 | 0.2.2 2013-07-15 12 | - Fix keyboard on OSX; disabled hiding Dock icon 13 | 14 | 0.2.1 2013-07-14 15 | - Save config to gui-config.json file on Windows 16 | - Fix incorrect window size on Windows 17 | - Hide Dock icon on OSX 18 | - Check for updates 19 | - Reduce binary size 20 | - Run gc() every 30s to reduce memory usage 21 | 22 | 0.2 2013-07-12 23 | - Support multiple profiles 24 | - Fix problems with restarting shadowsocks 25 | 26 | 0.1.5 2013-06-23 27 | - Package binary into a single exe/app 28 | 29 | 0.1.4 2013-06-22 30 | - Minimize on startup and after config is saved 31 | 32 | 0.1.3 2013-06-19 33 | - Update shadowsocks-nodejs 34 | 35 | 0.1.2 2013-06-18 36 | - Update shadowsocks-nodejs 37 | 38 | 0.1.1 2013-05-30 39 | - Auto-Complete Server IP 40 | 41 | 0.1 2013-05-29 42 | - Initial version 43 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | How to contribute 2 | ================= 3 | 4 | Before you submit issues, please take a few minutes to read this guide. 5 | 6 | 在你提交问题前,请花两分钟阅读一下这份指南。 7 | 8 | Issues 9 | ------ 10 | 11 | Please include the following information in your submission: 12 | 13 | 1. How did you set up your environment? (OS, version of Shadowsocks) 14 | 2. Where did you see this error, was it on local or on server? 15 | 3. What happened in your browser? Just no response, or any error message? 16 | 4. 10 lines of log on the local side of shadowsocks when the error happened. 17 | 5. 10 lines of log on the server side of shadowsocks when the error happened. 18 | 6. Any other useful information. 19 | 20 | Skip any of them if you don't know its meaning. 21 | 22 | 问题反馈 23 | ------- 24 | 25 | 请提交下面的信息: 26 | 27 | 1. 你是如何搭建环境的?(操作系统,Shadowsocks 版本) 28 | 2. 错误是发生在哪里,本地还是服务器? 29 | 3. 浏览器里的现象是什么?一直转菊花,还是有提示错误? 30 | 4. 发生错误时,本地端的十行完整的日志。 31 | 5. 发生错误时,服务器端的十行完整的日志。 32 | 6. 其它你认为可能和问题有关的信息。 33 | 34 | 如果你不清楚其中某条的含义, 可以直接跳过那一条。 35 | -------------------------------------------------------------------------------- /Cakefile: -------------------------------------------------------------------------------- 1 | {print} = require 'util' 2 | {spawn} = require 'child_process' 3 | 4 | build = () -> 5 | os = require 'os' 6 | if os.platform() == 'win32' 7 | coffeeCmd = 'coffee.cmd' 8 | else 9 | coffeeCmd = 'coffee' 10 | coffee = spawn coffeeCmd, ['-c', '-o', '.', 'src'] 11 | coffee.stderr.on 'data', (data) -> 12 | process.stderr.write data.toString() 13 | coffee.stdout.on 'data', (data) -> 14 | print data.toString() 15 | coffee.on 'exit', (code) -> 16 | if code != 0 17 | process.exit code 18 | 19 | task 'build', 'Build ./ from src/', -> 20 | build() 21 | 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | shadowsocks-gui 2 | 3 | Copyright (c) 2014 clowwindy 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | shadowsocks-gui 2 | =============== 3 | 4 | GUI for shadowsocks, powered by [shadowsocks-nodejs](https://github.com/clowwindy/shadowsocks-nodejs) 5 | 6 | OSX / Windows / Linux 7 | 8 | ![Screenshot](https://raw.github.com/shadowsocks/shadowsocks-gui/master/screenshot.png) 9 | 10 | Download 11 | -------- 12 | 13 | Download the latest version of your OS: 14 | 15 | https://sourceforge.net/projects/shadowsocksgui/files/dist/ 16 | 17 | Develop 18 | ------- 19 | 20 | Clone the repo and install dependencies: 21 | 22 | git clone https://github.com/shadowsocks/shadowsocks-gui.git 23 | cd shadowsocks-gui 24 | npm install 25 | 26 | Download [node-webkit](https://github.com/rogerwang/node-webkit#downloads) 27 | 28 | Then copy unzipped files into shadowsocks-gui directory. Then run nw.exe / node-webkit.app / nw 29 | 30 | See also: https://github.com/rogerwang/node-webkit/wiki/How-to-run-apps 31 | 32 | License 33 | -------- 34 | 35 | [MIT License](https://raw.github.com/shadowsocks/shadowsocks-gui/master/LICENSE) 36 | 37 | Server and other clients 38 | --------- 39 | 40 | Server and other clients can be found [here](https://github.com/clowwindy/shadowsocks/wiki/Ports-and-Clients). 41 | 42 | 43 | Bugs and Issues 44 | ---------------- 45 | Please visit [issue tracker](https://github.com/shadowsocks/shadowsocks-gui/issues?state=open) 46 | 47 | Mailing list: http://groups.google.com/group/shadowsocks 48 | -------------------------------------------------------------------------------- /args.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.7.1 2 | (function() { 3 | var allConfigs, defaultConfig, deleteConfig, fs, guiconfigFilename, loadConfig, loadConfigs, loadFromJSON, loadIndex, localStorage, publicConfig, saveConfig, saveConfigs, saveIndex, saveToJSON, util; 4 | 5 | localStorage = window.localStorage; 6 | 7 | util = require('util'); 8 | 9 | fs = require('fs'); 10 | 11 | guiconfigFilename = fs.realpathSync(process.execPath + '/..') + '/gui-config.json'; 12 | 13 | loadFromJSON = function() { 14 | var data, e, temp; 15 | if (process.platform === 'win32') { 16 | try { 17 | data = fs.readFileSync(guiconfigFilename); 18 | temp = JSON.parse(data.toString('utf-8')); 19 | if (temp.configs) { 20 | temp.configs = JSON.stringify(temp.configs); 21 | } 22 | localStorage = temp; 23 | return util.log('reading config file'); 24 | } catch (_error) { 25 | e = _error; 26 | return console.log(e); 27 | } 28 | } 29 | }; 30 | 31 | loadFromJSON(); 32 | 33 | saveToJSON = function() { 34 | var data, e, temp; 35 | if (process.platform === 'win32') { 36 | util.log('saving config file'); 37 | temp = JSON.parse(JSON.stringify(localStorage)); 38 | if (temp.configs) { 39 | temp.configs = JSON.parse(temp.configs); 40 | } 41 | data = JSON.stringify(temp, null, 2); 42 | try { 43 | return fs.writeFileSync(guiconfigFilename, data, { 44 | 'encoding': 'utf-8' 45 | }); 46 | } catch (_error) { 47 | e = _error; 48 | return util.log(e); 49 | } 50 | } 51 | }; 52 | 53 | publicConfig = { 54 | server: '209.141.36.62', 55 | server_port: 8348, 56 | local_port: 1080, 57 | password: '$#HAL9000!', 58 | method: 'aes-256-cfb', 59 | timeout: 600 60 | }; 61 | 62 | defaultConfig = { 63 | server_port: 8388, 64 | local_port: 1080, 65 | method: 'aes-256-cfb', 66 | timeout: 600 67 | }; 68 | 69 | loadConfigs = function() { 70 | var e; 71 | try { 72 | return JSON.parse(localStorage['configs'] || '[]'); 73 | } catch (_error) { 74 | e = _error; 75 | util.log(e); 76 | return []; 77 | } 78 | }; 79 | 80 | allConfigs = function() { 81 | var c, configs, e, i, result; 82 | if (localStorage['configs']) { 83 | result = []; 84 | try { 85 | configs = loadConfigs(); 86 | for (i in configs) { 87 | c = configs[i]; 88 | result.push("" + c.server + ":" + c.server_port); 89 | } 90 | return result; 91 | } catch (_error) { 92 | e = _error; 93 | } 94 | } 95 | return []; 96 | }; 97 | 98 | saveIndex = function(index) { 99 | localStorage['index'] = index; 100 | return saveToJSON(); 101 | }; 102 | 103 | loadIndex = function() { 104 | return +localStorage['index']; 105 | }; 106 | 107 | saveConfigs = function(configs) { 108 | localStorage['configs'] = JSON.stringify(configs); 109 | return saveToJSON(); 110 | }; 111 | 112 | saveConfig = function(index, config) { 113 | var configs; 114 | if (index === -1) { 115 | index = NaN; 116 | } 117 | configs = loadConfigs(); 118 | if (isNaN(index)) { 119 | configs.push(config); 120 | index = configs.length - 1; 121 | } else { 122 | configs[index] = config; 123 | } 124 | saveConfigs(configs); 125 | return index; 126 | }; 127 | 128 | loadConfig = function(index) { 129 | var configs; 130 | if (isNaN(index)) { 131 | return defaultConfig; 132 | } 133 | if (index === -1) { 134 | return publicConfig; 135 | } 136 | configs = loadConfigs(); 137 | return configs[index] || defaultConfig; 138 | }; 139 | 140 | deleteConfig = function(index) { 141 | var configs; 142 | if ((!isNaN(index)) && !(index === -1)) { 143 | configs = loadConfigs(); 144 | configs.splice(index, 1); 145 | return saveConfigs(configs); 146 | } 147 | }; 148 | 149 | exports.allConfigs = allConfigs; 150 | 151 | exports.saveConfig = saveConfig; 152 | 153 | exports.loadConfig = loadConfig; 154 | 155 | exports.deleteConfig = deleteConfig; 156 | 157 | exports.loadIndex = loadIndex; 158 | 159 | exports.saveIndex = saveIndex; 160 | 161 | exports.publicConfig = publicConfig; 162 | 163 | }).call(this); 164 | -------------------------------------------------------------------------------- /bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Bootstrap.js by @fat & @mdo 3 | * plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js 4 | * Copyright 2012 Twitter, Inc. 5 | * http://www.apache.org/licenses/LICENSE-2.0.txt 6 | */ 7 | !function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(){var a=this;this.$element.hide(),this.backdrop(function(){a.removeBackdrop(),a.$element.trigger("hidden")})},removeBackdrop:function(){this.$backdrop&&this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('