diff --git a/.github/workflows/exp.yml b/.github/workflows/exp.yml index f34ad862..ff860cb5 100644 --- a/.github/workflows/exp.yml +++ b/.github/workflows/exp.yml @@ -17,7 +17,7 @@ jobs: verStr: '1.3.x.x' boxAppend: box v2rayCorePkgName: 'v2ray-windows-32.zip' - v2rayCoreVer: 'v4.23.1' + v2rayCoreVer: 'v4.23.2' # https://github.com/v2ray/v2ray-core/releases/download/v4.20.0/v2ray-windows-32.zip v2rayCoreUrl: 'https://github.com/v2ray/v2ray-core/releases/download' diff --git a/.github/workflows/master.yml b/.github/workflows/master.yml index f098f0c1..f2a3099e 100644 --- a/.github/workflows/master.yml +++ b/.github/workflows/master.yml @@ -17,7 +17,7 @@ jobs: verStr: '1.3.x' boxAppend: box v2rayCorePkgName: 'v2ray-windows-32.zip' - v2rayCoreVer: 'v4.23.1' + v2rayCoreVer: 'v4.23.2' # https://github.com/v2ray/v2ray-core/releases/download/v4.20.0/v2ray-windows-32.zip v2rayCoreUrl: 'https://github.com/v2ray/v2ray-core/releases/download' diff --git a/Plugins/Luna/Controllers/FormEditorCtrl/AutoCompleteCtrl.cs b/Plugins/Luna/Controllers/FormEditorCtrl/AutoCompleteCtrl.cs index e7ef4c86..867b994c 100644 --- a/Plugins/Luna/Controllers/FormEditorCtrl/AutoCompleteCtrl.cs +++ b/Plugins/Luna/Controllers/FormEditorCtrl/AutoCompleteCtrl.cs @@ -24,7 +24,7 @@ internal sealed class AutoCompleteCtrl string KEY_FUNCTION = "funcs"; string KEY_VARS = "vars"; string KEY_MODULES = "modules"; - // string KEY_LINE_NUM = "line"; // line number + string KEY_LINE_NUM = "line"; // line number string KEY_METHODS = "methods"; string KEY_SUB_FUNCS = "subs"; #endregion @@ -537,8 +537,24 @@ string RemoveLocalPrefix(string text, bool trimFunctionPrefix) } bool ScrollToFunction(string text) + { + var funcs = funcDefTable; + if (funcs.ContainsKey(text)) + { + ScrollToLine(funcs[text]); + return true; + } + + return FallbackScrollToFunction(text); + } + + bool FallbackScrollToFunction(string text) { text = text?.Replace(":", ".")?.Replace(" ", "") + ?.Replace("['", ".") + ?.Replace("[\"", ".") + ?.Replace("\"]", "") + ?.Replace("']", "") ?.Split('(')?.FirstOrDefault(); if (string.IsNullOrWhiteSpace(text)) @@ -549,9 +565,9 @@ bool ScrollToFunction(string text) foreach (var line in editor.Lines) { var t = line.Text; - if (string.IsNullOrWhiteSpace(t) || !t.Contains("function")) - { - continue; + if (string.IsNullOrWhiteSpace(t) || !t.Contains("function")) + { + continue; } var trimed = RemoveLocalPrefix(t, true) @@ -600,9 +616,9 @@ void ScrollToDefinition(string text) return; } - if (ScrollToFunction(text) || ScrollToVariable(text)) - { - return; + if (ScrollToFunction(text) || ScrollToVariable(text)) + { + return; } foreach (var line in editor.Lines) @@ -695,6 +711,8 @@ void BindEvents() } + Dictionary funcDefTable = new Dictionary(); + private void OnCboxFunctionListDropDownHandler(object sender, EventArgs args) { history.Add(editor.CurrentLine); @@ -708,7 +726,8 @@ private void OnCboxFunctionListDropDownHandler(object sender, EventArgs args) KEY_METHODS, }; - List funcs = new List(); + Dictionary funcs = new Dictionary(); + foreach (var key in keys) { if (ast != null && ast[key] is JObject) @@ -716,17 +735,20 @@ private void OnCboxFunctionListDropDownHandler(object sender, EventArgs args) foreach (var kv in ast[key] as JObject) { var ps = (kv.Value as JObject)[KEY_PARAMS] as JArray; + var luaLineNumber = (kv.Value as JObject)[KEY_LINE_NUM].Value(); var sps = string.Join(", ", ps); - funcs.Add($"{kv.Key}({sps})"); + var fn = $"{kv.Key}({sps})"; + funcs.Add(fn, luaLineNumber - 1); } } } + funcDefTable = funcs; VgcApis.Misc.UI.Invoke(() => { var items = cboxFunctionList.Items; items.Clear(); - items.AddRange(funcs.OrderBy(x => x).ToArray()); + items.AddRange(funcs.Keys.OrderBy(x => x).ToArray()); VgcApis.Misc.UI.ResetComboBoxDropdownMenuWidth(cboxFunctionList); }); } @@ -770,6 +792,8 @@ bool ScrollToVariable(string v) return false; } + + private void OnCboxVarListDropDownHandler(object sender, EventArgs args) { history.Add(editor.CurrentLine); diff --git a/Plugins/Luna/Controllers/FormEditorCtrl/ButtonCtrl.cs b/Plugins/Luna/Controllers/FormEditorCtrl/ButtonCtrl.cs index 8f1d9f7a..cbf40a0b 100644 --- a/Plugins/Luna/Controllers/FormEditorCtrl/ButtonCtrl.cs +++ b/Plugins/Luna/Controllers/FormEditorCtrl/ButtonCtrl.cs @@ -115,6 +115,15 @@ public void Run( } #region public methods + public void LoadScript(string name) + { + if (!string.IsNullOrEmpty(name)) + { + cboxScriptName.Text = name; + CboxScriptNameChangedHandler(this, EventArgs.Empty); + } + } + public bool isLoadClrLib; public void KeyBoardShortcutHandler(KeyEventArgs keyEvent) @@ -218,13 +227,13 @@ public void SetCurFileName(string filename) } public void SetCurrentEditorContent(string content) => - VgcApis.Misc.UI.Invoke(() => editor.Text = content); - + VgcApis.Misc.UI.Invoke(() => editor.Text = content); + #endregion - + #region Scintilla - - + + void Scintilla_DoubleClick(object sender, EventArgs e) { var keyword = VgcApis.Misc.Utils.GetWordFromCurPos(editor); @@ -448,7 +457,6 @@ private void BindEvents() cboxScriptName.DropDown += (s, a) => ReloadScriptName(); cboxScriptName.SelectedValueChanged += CboxScriptNameChangedHandler; - } private void OnBtnSaveScriptClickHandler(bool showResult) diff --git a/Plugins/Luna/Controllers/FormEditorCtrl/MenuCtrl.cs b/Plugins/Luna/Controllers/FormEditorCtrl/MenuCtrl.cs index b4b9af8a..9c43fd54 100644 --- a/Plugins/Luna/Controllers/FormEditorCtrl/MenuCtrl.cs +++ b/Plugins/Luna/Controllers/FormEditorCtrl/MenuCtrl.cs @@ -51,10 +51,24 @@ public MenuCtrl( } public void Run( - Services.FormMgrSvc formMgrService) + Services.FormMgrSvc formMgrService, + Models.Data.LuaCoreSetting initialCoreSettings) { InitControls(); BindEvents(formMgrService); + + if (initialCoreSettings != null) + { + var name = initialCoreSettings.name; + if (!string.IsNullOrEmpty(name)) + { + editorCtrl.LoadScript(name); + } + + var enabled = initialCoreSettings.isLoadClr; + UpdateClrControlsEanbledState(enabled); + editorCtrl.isLoadClrLib = enabled; + } } #region private method @@ -71,9 +85,7 @@ private void BindEvents(FormMgrSvc formMgrService) miLoadClrLib.Click += (s, a) => { var enable = !miLoadClrLib.Checked; - miLoadClrLib.Checked = enable; - smiLbClrLib.Enabled = enable; - editorCtrl.isLoadClrLib = enable; + UpdateClrControlsEanbledState(enable); }; miNewWindow.Click += (s, a) => @@ -130,6 +142,13 @@ private void BindEvents(FormMgrSvc formMgrService) } }; } + + private void UpdateClrControlsEanbledState(bool enable) + { + miLoadClrLib.Checked = enable; + smiLbClrLib.Enabled = enable; + editorCtrl.isLoadClrLib = enable; + } #endregion } } diff --git a/Plugins/Luna/Controllers/FormMainCtrl/TabGeneralCtrl.cs b/Plugins/Luna/Controllers/FormMainCtrl/TabGeneralCtrl.cs index a2463d7b..b440b8df 100644 --- a/Plugins/Luna/Controllers/FormMainCtrl/TabGeneralCtrl.cs +++ b/Plugins/Luna/Controllers/FormMainCtrl/TabGeneralCtrl.cs @@ -29,10 +29,15 @@ public TabGeneralCtrl( #region public methods Services.LuaServer luaServer; + Services.FormMgrSvc formMgrSvc; - public void Run(Services.LuaServer luaServer) + public void Run( + Services.LuaServer luaServer, + Services.FormMgrSvc formMgrSvc) { this.luaServer = luaServer; + this.formMgrSvc = formMgrSvc; + BindEvents(luaServer); BindDragDropEvent(); @@ -221,8 +226,8 @@ void AddLuaCoreCtrlToPanel() var ctrls = luaServer.GetAllLuaCoreCtrls(); foreach (var c in ctrls) { - var ui = new Views.UserControls.LuaUI( - luaServer, c); + var ui = new LuaUI( + luaServer, formMgrSvc, c); flyLuaUiPanel.Controls.Add(ui); } } diff --git a/Plugins/Luna/Controllers/LuaCoreCtrl.cs b/Plugins/Luna/Controllers/LuaCoreCtrl.cs index e9e17b6a..84564137 100644 --- a/Plugins/Luna/Controllers/LuaCoreCtrl.cs +++ b/Plugins/Luna/Controllers/LuaCoreCtrl.cs @@ -132,7 +132,6 @@ public bool isRunning } } - void InvokeOnStateChangeIgnoreError() { try @@ -144,7 +143,8 @@ void InvokeOnStateChangeIgnoreError() #endregion #region public methods - public string GetScript() => coreSetting.script; + public Models.Data.LuaCoreSetting GetCoreSettings() => + coreSetting; public void SetScriptName(string name) { @@ -169,9 +169,9 @@ public void Stop() luaSys?.OnSignalStop(); } - public void Abort() => Killer(2000); + public void Abort() => KillCore(2000); - public void AbortNow() => Killer(1); + public void AbortNow() => KillCore(1); public void Start() { @@ -200,7 +200,7 @@ public void Cleanup() #endregion #region private methods - void Killer(int timeout) + void KillCore(int timeout) { if (!isRunning) { diff --git a/Plugins/Luna/Libs/LuaSnippet/BestMatchSnippets.cs b/Plugins/Luna/Libs/LuaSnippet/BestMatchSnippets.cs index 600b9835..c2e99507 100644 --- a/Plugins/Luna/Libs/LuaSnippet/BestMatchSnippets.cs +++ b/Plugins/Luna/Libs/LuaSnippet/BestMatchSnippets.cs @@ -60,6 +60,9 @@ public void UpdateRequireModuleSnippets(List snippets) #endregion #region private methods + HashSet ignoredList = + new HashSet( + VgcApis.Models.Consts.Lua.LuaKeywords.Split(' ')); private IEnumerable BuildList() { @@ -69,6 +72,20 @@ private IEnumerable BuildList() fragment = VgcApis.Misc.Utils.GetFragment(editor, searchPattern); }); + var snps = new List(); + if (!ignoredList.Contains(fragment)) + { + snps = CreateSnippets(fragment); + } + + //return autocomplete items + foreach (var item in snps) + yield return item; + } + + private List CreateSnippets(string fragment) + { + List snps; var cache = customScriptSnippets; List candidates = cache @@ -85,15 +102,12 @@ private IEnumerable BuildList() } } - var sorted = table + snps = table .OrderBy(kv => kv.Value) .ThenBy(kv => kv.Key.GetLowerText()) .Select(kv => kv.Key as AutocompleteItem) .ToList(); - - //return autocomplete items - foreach (var item in sorted) - yield return item; + return snps; } List GenCandidateList(string fragment) diff --git a/Plugins/Luna/Libs/LuaSnippet/SnippetsCache.cs b/Plugins/Luna/Libs/LuaSnippet/SnippetsCache.cs index 5a595561..712ea10a 100644 --- a/Plugins/Luna/Libs/LuaSnippet/SnippetsCache.cs +++ b/Plugins/Luna/Libs/LuaSnippet/SnippetsCache.cs @@ -37,11 +37,6 @@ public BestMatchSnippets CreateBestMatchSnippets(ScintillaNET.Scintilla editor) #endregion #region private methods - string GetFilteredLuaKeywords() => - VgcApis.Models.Consts.Lua.LuaKeywords - .Replace("do", "") - .Replace("then", "") - .Replace("end", ""); List GetAllNameapaces() => VgcApis.Misc.Utils.GetAllAssembliesType() .Select(t => t.Namespace) @@ -93,7 +88,7 @@ List GenKeywords( IEnumerable initValues) => new StringBuilder(VgcApis.Models.Consts.Lua.LuaModules) .Append(@" ") - .Append(GetFilteredLuaKeywords()) + .Append(VgcApis.Models.Consts.Lua.LuaKeywords) .ToString() .Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) .Union(initValues) diff --git a/Plugins/Luna/Libs/Sys/VolumeChanger.cs b/Plugins/Luna/Libs/Sys/VolumeChanger.cs new file mode 100644 index 00000000..3c83b8bb --- /dev/null +++ b/Plugins/Luna/Libs/Sys/VolumeChanger.cs @@ -0,0 +1,40 @@ +using System; +using System.Runtime.InteropServices; + +namespace Luna.Libs.Sys +{ + public static class VolumeChanger + { + // https://stackoverflow.com/questions/35597458/set-system-volume-windows-10-in-c-sharp + + private const byte VK_VOLUME_MUTE = 0xAD; + private const byte VK_VOLUME_DOWN = 0xAE; + private const byte VK_VOLUME_UP = 0xAF; + private const UInt32 KEYEVENTF_EXTENDEDKEY = 0x0001; + private const UInt32 KEYEVENTF_KEYUP = 0x0002; + + [DllImport("user32.dll")] + static extern void keybd_event(byte bVk, byte bScan, UInt32 dwFlags, UInt32 dwExtraInfo); + + [DllImport("user32.dll")] + static extern Byte MapVirtualKey(UInt32 uCode, UInt32 uMapType); + + public static void VolumeUp() + { + keybd_event(VK_VOLUME_UP, MapVirtualKey(VK_VOLUME_UP, 0), KEYEVENTF_EXTENDEDKEY, 0); + keybd_event(VK_VOLUME_UP, MapVirtualKey(VK_VOLUME_UP, 0), KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + + public static void VolumeDown() + { + keybd_event(VK_VOLUME_DOWN, MapVirtualKey(VK_VOLUME_DOWN, 0), KEYEVENTF_EXTENDEDKEY, 0); + keybd_event(VK_VOLUME_DOWN, MapVirtualKey(VK_VOLUME_DOWN, 0), KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + + public static void Mute() + { + keybd_event(VK_VOLUME_MUTE, MapVirtualKey(VK_VOLUME_MUTE, 0), KEYEVENTF_EXTENDEDKEY, 0); + keybd_event(VK_VOLUME_MUTE, MapVirtualKey(VK_VOLUME_MUTE, 0), KEYEVENTF_EXTENDEDKEY | KEYEVENTF_KEYUP, 0); + } + } +} diff --git a/Plugins/Luna/Luna.csproj b/Plugins/Luna/Luna.csproj index b5f91729..7377ace0 100644 --- a/Plugins/Luna/Luna.csproj +++ b/Plugins/Luna/Luna.csproj @@ -90,6 +90,7 @@ + @@ -100,6 +101,7 @@ + diff --git a/Plugins/Luna/Models/Apis/Components/Web.cs b/Plugins/Luna/Models/Apis/Components/Web.cs index e6104702..f2336282 100644 --- a/Plugins/Luna/Models/Apis/Components/Web.cs +++ b/Plugins/Luna/Models/Apis/Components/Web.cs @@ -1,4 +1,8 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; namespace Luna.Models.Apis.Components { @@ -18,6 +22,31 @@ public Web(VgcApis.Interfaces.Services.IApiService api) } #region ILuaWeb thinggy + public string Post(string url, string text) => Post(url, text, 20000); + + public string Post(string url, string text, int timeout) + { + timeout = Math.Max(1, timeout); + + try + { + var t = Task.Run(async () => + { + using (var client = new HttpClient()) + { + var token = new CancellationTokenSource(timeout).Token; + var content = new StringContent(text); + var resp = await client.PostAsync(url, content, token); + return await resp.Content.ReadAsStringAsync(); + } + }); + + return t.GetAwaiter().GetResult(); + } + catch { } + return null; + } + public bool Tcping(string url, int milSec) => Tcping(url, milSec, -1); diff --git a/Plugins/Luna/Models/Apis/LuaSys.cs b/Plugins/Luna/Models/Apis/LuaSys.cs index 7343d786..e1c78e83 100644 --- a/Plugins/Luna/Models/Apis/LuaSys.cs +++ b/Plugins/Luna/Models/Apis/LuaSys.cs @@ -70,6 +70,32 @@ void TrackdownProcess(Process proc) } #endregion + #region ILuaSys.Net + List httpServs = new List(); + + public VgcApis.Interfaces.Lua.IRunnable CreateHttpServer( + string url, + VgcApis.Interfaces.Lua.ILuaMailBox inbox, + VgcApis.Interfaces.Lua.ILuaMailBox outbox) + { + try + { + var serv = new SysCmpos.HttpServer(url, inbox, outbox); + lock (httpServs) + { + httpServs.Add(serv); + } + return serv; + } + catch (Exception ex) + { + luaApis.SendLog(ex.ToString()); + throw; + } + } + + #endregion + #region ILluaSys.Hotkey public string GetAllKeyNames() { @@ -276,7 +302,7 @@ public Process RunAndForgot(string exePath, string args, string stdin) => public Process RunAndForgot(string exePath, string args, string stdin, LuaTable envs, bool hasWindow, bool redirectOutput) => - RunProcWorker(false, exePath, args, stdin, envs, hasWindow, redirectOutput); + RunProcWrapper(false, exePath, args, stdin, envs, hasWindow, redirectOutput); public Process Run(string exePath) => Run(exePath, null); @@ -289,11 +315,17 @@ public Process Run(string exePath, string args, string stdin) => public Process Run(string exePath, string args, string stdin, LuaTable envs, bool hasWindow, bool redirectOutput) => - RunProcWorker(true, exePath, args, stdin, envs, hasWindow, redirectOutput); + RunProcWrapper(true, exePath, args, stdin, envs, hasWindow, redirectOutput); #endregion #region ILuaSys.System + public void VolumeUp() => Libs.Sys.VolumeChanger.VolumeUp(); + + public void VolumeDown() => Libs.Sys.VolumeChanger.VolumeDown(); + + public void VolumeMute() => Libs.Sys.VolumeChanger.Mute(); + static string osReleaseId; public string GetOsReleaseInfo() { @@ -349,8 +381,21 @@ public void OnSignalStop() #endregion #region private methods - Process RunProcWorker(bool isTracking, string exePath, string args, string stdin, + Process RunProcWrapper( + bool isTracking, string exePath, string args, string stdin, LuaTable envs, bool hasWindow, bool redirectOutput) + { + try + { + return RunProcWorker(isTracking, exePath, args, stdin, envs, hasWindow, redirectOutput); + } + catch { } + return null; + } + + Process RunProcWorker( + bool isTracking, string exePath, string args, string stdin, + LuaTable envs, bool hasWindow, bool redirectOutput) { var useStdIn = !string.IsNullOrEmpty(stdin); var p = new Process @@ -430,6 +475,21 @@ private void KillAllProcesses() } } + void CloseAllHttpServers() + { + List servs; + lock (httpServs) + { + servs = httpServs.ToList(); + httpServs.Clear(); + } + + foreach (var s in servs) + { + s.Stop(); + } + } + void CloseAllMailBox() { List boxes; @@ -462,6 +522,7 @@ void RemoveAllKeyboardHooks() protected override void Cleanup() { RemoveAllKeyboardHooks(); + CloseAllHttpServers(); CloseAllMailBox(); KillAllProcesses(); } diff --git a/Plugins/Luna/Models/Apis/SysCmpos/HttpServer.cs b/Plugins/Luna/Models/Apis/SysCmpos/HttpServer.cs new file mode 100644 index 00000000..d231393f --- /dev/null +++ b/Plugins/Luna/Models/Apis/SysCmpos/HttpServer.cs @@ -0,0 +1,166 @@ +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Net; + +namespace Luna.Models.Apis.SysCmpos +{ + public class HttpServer : + VgcApis.BaseClasses.Disposable, + VgcApis.Interfaces.Lua.IRunnable + { + HttpListener serv; + + private readonly VgcApis.Interfaces.Lua.ILuaMailBox inbox; + private readonly VgcApis.Interfaces.Lua.ILuaMailBox outbox; + + public HttpServer( + string url, + VgcApis.Interfaces.Lua.ILuaMailBox inbox, + VgcApis.Interfaces.Lua.ILuaMailBox outbox) + { + this.inbox = inbox; + this.outbox = outbox; + serv = new HttpListener(); + serv.Prefixes.Add(url); + } + + #region public methods + public void Start() + { + try + { + Stop(); + serv.Start(); + HandleConnOut(); + HandleConnIn(); + } + catch { } + } + + public void Stop() + { + try + { + serv.Stop(); + } + catch { } + } + #endregion + + #region protected + protected override void Cleanup() + { + Stop(); + } + #endregion + + #region private methods + const int MaxContextLen = 10240; + + ConcurrentDictionary contexts = new ConcurrentDictionary(); + + void HandleConnOut() + { + VgcApis.Misc.Utils.RunInBackground(() => + { + try + { + while (true) + { + var mail = outbox.Wait(); + if (mail == null) + { + break; + } + + if (!contexts.TryRemove(mail.title, out var ctx)) + { + continue; + } + + HandleOneConnOut(ctx, mail.GetContent()); + } + } + catch { } + }); + } + + void HandleOneConnOut(HttpListenerContext ctx, string content) + { + VgcApis.Misc.Utils.RunInBackground(() => + { + try + { + var resp = ctx.Response; + var encoding = ctx.Request.ContentEncoding; + var buff = encoding.GetBytes(content ?? ""); + resp.ContentLength64 = buff.Length; + using (var s = resp.OutputStream) + { + s.Write(buff, 0, buff.Length); + } + resp.Close(); + } + catch { } + }); + } + + void HandleConnIn() + { + VgcApis.Misc.Utils.RunInBackground(() => + { + try + { + while (serv.IsListening) + { + if (contexts.Keys.Count > MaxContextLen) + { + VgcApis.Misc.Utils.Sleep(100); + continue; + } + + var ctx = serv.GetContext(); + try + { + HandleOneConnection(ctx); + } + catch { } + } + } + catch { }; + }); + } + + int HttpMethodToCode(string method) + { + switch (method) + { + case "POST": + return 1; + default: + return 0; + } + } + + void HandleOneConnection(HttpListenerContext ctx) + { + var req = ctx.Request; + var code = HttpMethodToCode(req.HttpMethod); + + string text; + using (var reader = new StreamReader(req.InputStream, req.ContentEncoding)) + { + text = reader.ReadToEnd(); + } + + var id = Guid.NewGuid().ToString(); + contexts.TryAdd(id, ctx); + inbox.Send(inbox.GetAddress(), code, id, true, text ?? ""); + } + + #endregion + + + } +} diff --git a/Plugins/Luna/Properties/Resources.Designer.cs b/Plugins/Luna/Properties/Resources.Designer.cs index 076f186f..c01cc2c9 100644 --- a/Plugins/Luna/Properties/Resources.Designer.cs +++ b/Plugins/Luna/Properties/Resources.Designer.cs @@ -130,7 +130,7 @@ internal static System.Drawing.Bitmap StoredProcedureScript_16x { } /// - /// 查找类似 0.3.7 的本地化字符串。 + /// 查找类似 0.4.1 的本地化字符串。 /// internal static string Version { get { diff --git a/Plugins/Luna/Properties/Resources.resx b/Plugins/Luna/Properties/Resources.resx index 4af0ff56..a87433b0 100644 --- a/Plugins/Luna/Properties/Resources.resx +++ b/Plugins/Luna/Properties/Resources.resx @@ -140,7 +140,7 @@ ..\Resources\Icons\App\StoredProcedureScript_16x.png;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - 0.3.7 - new static parser + 0.4.1 + fix a bug which would block core update. \ No newline at end of file diff --git a/Plugins/Luna/Resources/Files/LuaPredefinedFunctions.txt b/Plugins/Luna/Resources/Files/LuaPredefinedFunctions.txt index f459b769..5332ca9e 100644 --- a/Plugins/Luna/Resources/Files/LuaPredefinedFunctions.txt +++ b/Plugins/Luna/Resources/Files/LuaPredefinedFunctions.txt @@ -1,6 +1,16 @@ -- Enable import on 2020-04-17 -- import = function () end +-- require path +package.path = ".\\?.lua;.\\?\\init.lua" + +-- dll path +if os.getenv('PROCESSOR_ARCHITECTURE') == "AMD64" then + package.cpath = '.\\lua\\dll64\\?.dll' +else + package.cpath = '.\\lua\\dll32\\?.dll' +end + print = function(...) Misc:Print(...) end os.exit = function() print("os.exit() is disabled") end @@ -88,8 +98,10 @@ table.dump = function(t, indent, header, level) if tt ~= "table" then if tt == "string" then return '"' .. t .. '"' - else + elseif tt == "nil" or tt == "boolean" or tt == "number" then return tostring(t) + else + return tt end end diff --git a/Plugins/Luna/Resources/Icons/editor/HamburgerMenu_16x.png b/Plugins/Luna/Resources/Icons/editor/HamburgerMenu_16x.png new file mode 100644 index 00000000..d1f84c70 Binary files /dev/null and b/Plugins/Luna/Resources/Icons/editor/HamburgerMenu_16x.png differ diff --git a/Plugins/Luna/Services/FormMgrSvc.cs b/Plugins/Luna/Services/FormMgrSvc.cs index f666fc8d..adb9a92a 100644 --- a/Plugins/Luna/Services/FormMgrSvc.cs +++ b/Plugins/Luna/Services/FormMgrSvc.cs @@ -29,14 +29,19 @@ public void Run( } #region public methods + public void CreateNewEditor() => CreateNewEditor(null); - public void CreateNewEditor() + public void CreateNewEditor(Models.Data.LuaCoreSetting initialCoreSettings) { lock (formLocker) { VgcApis.Misc.UI.Invoke(() => { - var newForm = Views.WinForms.FormEditor.CreateForm(api, settings, luaServer, this); + var newForm = Views.WinForms.FormEditor.CreateForm( + api, settings, luaServer, this, + + initialCoreSettings); + newForm.FormClosed += (s, a) => { var form = newForm; // capture @@ -95,11 +100,6 @@ void RemoveFormFromList(Views.WinForms.FormEditor form) #region protected methods protected override void Cleanup() { - if (settings.IsClosing()) - { - return; - } - VgcApis.Misc.UI.CloseFormIgnoreError(formMain); List formList; diff --git a/Plugins/Luna/Services/LuaServer.cs b/Plugins/Luna/Services/LuaServer.cs index 98b44979..a4de0251 100644 --- a/Plugins/Luna/Services/LuaServer.cs +++ b/Plugins/Luna/Services/LuaServer.cs @@ -56,10 +56,8 @@ public List GetAllScripts() var ctrls = GetAllLuaCoreCtrls(); foreach (var luaCore in ctrls) { - scripts.Add(new string[] { - luaCore.name, - luaCore.GetScript(), - }); + var cs = luaCore.GetCoreSettings(); + scripts.Add(new string[] { cs.name, cs.script }); } return scripts; } @@ -173,11 +171,6 @@ public bool AddOrReplaceScript(string name, string script) #region protected methods protected override void Cleanup() { - if (settings.IsClosing()) - { - return; - } - var coreCtrls = GetAllLuaCoreCtrls(); foreach (var ctrl in coreCtrls) { diff --git a/Plugins/Luna/Services/MenuUpdater.cs b/Plugins/Luna/Services/MenuUpdater.cs index 7e52f62b..0f68db6f 100644 --- a/Plugins/Luna/Services/MenuUpdater.cs +++ b/Plugins/Luna/Services/MenuUpdater.cs @@ -70,7 +70,19 @@ List GenSubMenuItems() foreach (var luaCtrl in luaCtrls) { var ctrl = luaCtrl; // capture - var mi = new ToolStripMenuItem(ctrl.name, null, (s, a) => ctrl.Start()); + Action onClick = () => + { + if (ctrl.isRunning) + { + ctrl.Stop(); + } + else + { + ctrl.Start(); + } + }; + + var mi = new ToolStripMenuItem(ctrl.name, null, (s, a) => onClick()); mi.Checked = luaCtrl.isRunning; mis.Add(mi); } diff --git a/Plugins/Luna/Services/Settings.cs b/Plugins/Luna/Services/Settings.cs index fe67f045..0cab492a 100644 --- a/Plugins/Luna/Services/Settings.cs +++ b/Plugins/Luna/Services/Settings.cs @@ -16,6 +16,9 @@ internal class Settings : public Settings() { } #region properties + bool _isDisposing = false; + public void SetIsDisposing(bool value) => _isDisposing = value; + public bool isScreenLocked => vgcSetting.IsScreenLocked(); @@ -36,10 +39,7 @@ public void SendLog(string contnet) vgcSetting.SendLog(string.Format("[{0}] {1}", name, contnet)); } - bool isDisposing = false; - public bool IsClosing() => isDisposing || vgcSetting.IsClosing(); - - public void SetIsDisposing(bool value) => isDisposing = value; + public bool IsClosing() => _isDisposing || vgcSetting.IsClosing(); public void Run( VgcApis.Interfaces.Services.ISettingsService vgcSetting) diff --git a/Plugins/Luna/Views/UserControls/LuaUI.Designer.cs b/Plugins/Luna/Views/UserControls/LuaUI.Designer.cs index d579e12e..dba112fb 100644 --- a/Plugins/Luna/Views/UserControls/LuaUI.Designer.cs +++ b/Plugins/Luna/Views/UserControls/LuaUI.Designer.cs @@ -32,12 +32,21 @@ private void InitializeComponent() System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(LuaUI)); this.btnRun = new System.Windows.Forms.Button(); this.btnStop = new System.Windows.Forms.Button(); - this.btnKill = new System.Windows.Forms.Button(); this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this.lbRunningState = new System.Windows.Forms.Label(); this.lbName = new System.Windows.Forms.Label(); - this.btnRemove = new System.Windows.Forms.Button(); this.rlbOptions = new VgcApis.UserControls.RoundLabel(); + this.btnMenuMore = new System.Windows.Forms.Button(); + this.contextMenuStripMore = new System.Windows.Forms.ContextMenuStrip(this.components); + this.actionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.restartToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.stopToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.terminateToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripMenuItem1 = new System.Windows.Forms.ToolStripSeparator(); + this.editToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.optionToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.contextMenuStripMore.SuspendLayout(); this.SuspendLayout(); // // btnRun @@ -58,15 +67,6 @@ private void InitializeComponent() this.btnStop.UseVisualStyleBackColor = true; this.btnStop.Click += new System.EventHandler(this.btnStop_Click); // - // btnKill - // - resources.ApplyResources(this.btnKill, "btnKill"); - this.btnKill.Cursor = System.Windows.Forms.Cursors.Default; - this.btnKill.Name = "btnKill"; - this.toolTip1.SetToolTip(this.btnKill, resources.GetString("btnKill.ToolTip")); - this.btnKill.UseVisualStyleBackColor = true; - this.btnKill.Click += new System.EventHandler(this.btnKill_Click); - // // lbRunningState // this.lbRunningState.Cursor = System.Windows.Forms.Cursors.SizeAll; @@ -85,15 +85,6 @@ private void InitializeComponent() this.toolTip1.SetToolTip(this.lbName, resources.GetString("lbName.ToolTip")); this.lbName.MouseDown += new System.Windows.Forms.MouseEventHandler(this.lbName_MouseDown); // - // btnRemove - // - resources.ApplyResources(this.btnRemove, "btnRemove"); - this.btnRemove.Cursor = System.Windows.Forms.Cursors.Default; - this.btnRemove.Name = "btnRemove"; - this.toolTip1.SetToolTip(this.btnRemove, resources.GetString("btnRemove.ToolTip")); - this.btnRemove.UseVisualStyleBackColor = true; - this.btnRemove.Click += new System.EventHandler(this.btnRemove_Click); - // // rlbOptions // this.rlbOptions._BackColor = System.Drawing.Color.Wheat; @@ -104,16 +95,85 @@ private void InitializeComponent() this.toolTip1.SetToolTip(this.rlbOptions, resources.GetString("rlbOptions.ToolTip")); this.rlbOptions.Click += new System.EventHandler(this.rlbOptions_Click); // + // btnMenuMore + // + resources.ApplyResources(this.btnMenuMore, "btnMenuMore"); + this.btnMenuMore.Cursor = System.Windows.Forms.Cursors.Default; + this.btnMenuMore.Name = "btnMenuMore"; + this.toolTip1.SetToolTip(this.btnMenuMore, resources.GetString("btnMenuMore.ToolTip")); + this.btnMenuMore.UseVisualStyleBackColor = true; + this.btnMenuMore.Click += new System.EventHandler(this.btnMenuMore_Click); + // + // contextMenuStripMore + // + this.contextMenuStripMore.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.actionToolStripMenuItem, + this.toolStripMenuItem1, + this.editToolStripMenuItem, + this.optionToolStripMenuItem, + this.removeToolStripMenuItem}); + this.contextMenuStripMore.Name = "contextMenuStripMore"; + resources.ApplyResources(this.contextMenuStripMore, "contextMenuStripMore"); + // + // actionToolStripMenuItem + // + this.actionToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.restartToolStripMenuItem, + this.stopToolStripMenuItem, + this.terminateToolStripMenuItem}); + this.actionToolStripMenuItem.Name = "actionToolStripMenuItem"; + resources.ApplyResources(this.actionToolStripMenuItem, "actionToolStripMenuItem"); + // + // restartToolStripMenuItem + // + this.restartToolStripMenuItem.Name = "restartToolStripMenuItem"; + resources.ApplyResources(this.restartToolStripMenuItem, "restartToolStripMenuItem"); + this.restartToolStripMenuItem.Click += new System.EventHandler(this.restartToolStripMenuItem_Click); + // + // stopToolStripMenuItem + // + this.stopToolStripMenuItem.Name = "stopToolStripMenuItem"; + resources.ApplyResources(this.stopToolStripMenuItem, "stopToolStripMenuItem"); + this.stopToolStripMenuItem.Click += new System.EventHandler(this.stopToolStripMenuItem_Click); + // + // terminateToolStripMenuItem + // + this.terminateToolStripMenuItem.Name = "terminateToolStripMenuItem"; + resources.ApplyResources(this.terminateToolStripMenuItem, "terminateToolStripMenuItem"); + this.terminateToolStripMenuItem.Click += new System.EventHandler(this.terminateToolStripMenuItem_Click); + // + // toolStripMenuItem1 + // + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; + resources.ApplyResources(this.toolStripMenuItem1, "toolStripMenuItem1"); + // + // editToolStripMenuItem + // + this.editToolStripMenuItem.Name = "editToolStripMenuItem"; + resources.ApplyResources(this.editToolStripMenuItem, "editToolStripMenuItem"); + this.editToolStripMenuItem.Click += new System.EventHandler(this.editToolStripMenuItem_Click); + // + // optionToolStripMenuItem + // + this.optionToolStripMenuItem.Name = "optionToolStripMenuItem"; + resources.ApplyResources(this.optionToolStripMenuItem, "optionToolStripMenuItem"); + this.optionToolStripMenuItem.Click += new System.EventHandler(this.optionToolStripMenuItem_Click); + // + // removeToolStripMenuItem + // + this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; + resources.ApplyResources(this.removeToolStripMenuItem, "removeToolStripMenuItem"); + this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); + // // LuaUI // resources.ApplyResources(this, "$this"); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; this.Controls.Add(this.rlbOptions); - this.Controls.Add(this.btnRemove); + this.Controls.Add(this.btnMenuMore); this.Controls.Add(this.lbName); this.Controls.Add(this.lbRunningState); - this.Controls.Add(this.btnKill); this.Controls.Add(this.btnStop); this.Controls.Add(this.btnRun); this.Cursor = System.Windows.Forms.Cursors.SizeAll; @@ -121,6 +181,7 @@ private void InitializeComponent() this.toolTip1.SetToolTip(this, resources.GetString("$this.ToolTip")); this.Load += new System.EventHandler(this.LuaUI_Load); this.MouseDown += new System.Windows.Forms.MouseEventHandler(this.LuaUI_MouseDown); + this.contextMenuStripMore.ResumeLayout(false); this.ResumeLayout(false); } @@ -129,11 +190,19 @@ private void InitializeComponent() private System.Windows.Forms.Button btnRun; private System.Windows.Forms.Button btnStop; - private System.Windows.Forms.Button btnKill; private System.Windows.Forms.ToolTip toolTip1; private System.Windows.Forms.Label lbRunningState; private System.Windows.Forms.Label lbName; - private System.Windows.Forms.Button btnRemove; private VgcApis.UserControls.RoundLabel rlbOptions; + private System.Windows.Forms.Button btnMenuMore; + private System.Windows.Forms.ContextMenuStrip contextMenuStripMore; + private System.Windows.Forms.ToolStripMenuItem actionToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem restartToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem stopToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem terminateToolStripMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem editToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem optionToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; } } diff --git a/Plugins/Luna/Views/UserControls/LuaUI.cs b/Plugins/Luna/Views/UserControls/LuaUI.cs index a8c1ccb3..baf9dc2c 100644 --- a/Plugins/Luna/Views/UserControls/LuaUI.cs +++ b/Plugins/Luna/Views/UserControls/LuaUI.cs @@ -1,4 +1,5 @@ using Luna.Resources.Langs; +using Luna.Services; using System; using System.Drawing; using System.Windows.Forms; @@ -9,15 +10,17 @@ internal partial class LuaUI : UserControl { Controllers.LuaCoreCtrl luaCoreCtrl; Services.LuaServer luaServer; - + private readonly FormMgrSvc formMgrSvc; VgcApis.Libs.Tasks.LazyGuy lazyUpdater; public LuaUI( Services.LuaServer luaServer, + Services.FormMgrSvc formMgrSvc, Controllers.LuaCoreCtrl luaCoreCtrl) { this.luaCoreCtrl = luaCoreCtrl; this.luaServer = luaServer; + this.formMgrSvc = formMgrSvc; InitializeComponent(); } @@ -112,10 +115,7 @@ void UpdateRunningState() #endregion #region UI event handlers - private void btnKill_Click(object sender, EventArgs e) - { - luaCoreCtrl.Abort(); - } + private void btnStop_Click(object sender, EventArgs e) { @@ -127,23 +127,6 @@ private void btnRun_Click(object sender, EventArgs e) luaCoreCtrl.Start(); } - private void btnRemove_Click(object sender, EventArgs e) - { - var scriptName = luaCoreCtrl.name; - if (string.IsNullOrEmpty(scriptName) - || !VgcApis.Misc.UI.Confirm(I18N.ConfirmRemoveScript)) - { - return; - } - - VgcApis.Misc.Utils.RunInBackground(() => - { - if (!luaServer.RemoveScriptByName(scriptName)) - { - VgcApis.Misc.UI.MsgBoxAsync("", I18N.ScriptNotFound); - } - }); - } private void LuaUI_MouseDown(object sender, MouseEventArgs e) { @@ -165,8 +148,62 @@ private void rlbOptions_Click(object sender, EventArgs e) Views.WinForms.FormLuaCoreSettings.ShowForm(luaCoreCtrl); } - #endregion + private void restartToolStripMenuItem_Click(object sender, EventArgs e) + { + luaCoreCtrl.Start(); + } + + private void stopToolStripMenuItem_Click(object sender, EventArgs e) + { + luaCoreCtrl.Stop(); + } + + private void terminateToolStripMenuItem_Click(object sender, EventArgs e) + { + luaCoreCtrl.Abort(); + } + + private void editToolStripMenuItem_Click(object sender, EventArgs e) + { + var cs = luaCoreCtrl.GetCoreSettings(); + formMgrSvc.CreateNewEditor(cs); + } + + private void optionToolStripMenuItem_Click(object sender, EventArgs e) + { + Views.WinForms.FormLuaCoreSettings.ShowForm(luaCoreCtrl); + + } + + private void removeToolStripMenuItem_Click(object sender, EventArgs e) + { + var scriptName = luaCoreCtrl.name; + if (string.IsNullOrEmpty(scriptName) + || !VgcApis.Misc.UI.Confirm(I18N.ConfirmRemoveScript)) + { + return; + } + + VgcApis.Misc.Utils.RunInBackground(() => + { + if (!luaServer.RemoveScriptByName(scriptName)) + { + VgcApis.Misc.UI.MsgBoxAsync("", I18N.ScriptNotFound); + } + }); + + } + + private void btnMenuMore_Click(object sender, EventArgs e) + { + var control = sender as Control; + Point pos = new Point(control.Left, control.Top + control.Height); + contextMenuStripMore.Show(this, pos); + } + + + #endregion } } diff --git a/Plugins/Luna/Views/UserControls/LuaUI.resx b/Plugins/Luna/Views/UserControls/LuaUI.resx index 40872da1..8a3310aa 100644 --- a/Plugins/Luna/Views/UserControls/LuaUI.resx +++ b/Plugins/Luna/Views/UserControls/LuaUI.resx @@ -132,7 +132,7 @@ Zoom - 272, 4 + 290, 4 2, 2, 2, 2 @@ -179,7 +179,7 @@ NoControl - 292, 4 + 311, 4 2, 2, 2, 2 @@ -205,45 +205,6 @@ 5 - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - xAAADsQBlSsOGwAAAKpJREFUOE/dU0sOQDAU7CEcwIWcgEW3Entns+EIPYUNEolFt/WGJ9GnxGdnklm0 - b2bSMlUS1lpNbIhOEHuaZUfQMCaaqa5clyaujZRH7GEGDbRsW8Hmsc+zg1FyKAuEjF4ILQwGIUOIHGI2 - s8bRQsIr8nU0Ahp55zPsNfDAiwBvAJ5B6uD9ScDnj/jtN3IXzJ0WbvSKBNBiqfKdNgarDHDIu8e0Bw0f - PGelZoKjbA1QHzFCAAAAAElFTkSuQmCC - - - - Zoom - - - 312, 4 - - - 2, 2, 2, 2 - - - 20, 20 - - - 3 - - - Terminate this script. - - - btnKill - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 4 - 宋体, 9pt, style=Bold @@ -278,13 +239,13 @@ $this - 3 + 4 35, 8 - 232, 13 + 250, 13 7 @@ -308,49 +269,7 @@ $this - 2 - - - - iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO - xAAADsQBlSsOGwAAALZJREFUOE9jQAbfvn0zgDJxApxqgBIVQPwfiDOhQhgAJAdVUwEVggCQwKubV/9v - i3T///b+HayGgMRAcltCHP4/v3gWYQiQYQDEYM0L1Xj/r3XWxTAEphkkB1IDMgSkB6QXqwJkQ/DJgTXD - AC6FRGmGAWyGEK0ZBtANIUkzCFBkALpmEI3MxmsINs0gPjYxDENwaQaJ45ODaaZKQiI/KcMASACIsWqG - AZAcVA2qZhgASpCYnRkYAKjTuVoFkX4yAAAAAElFTkSuQmCC - - - - Zoom - - - NoControl - - - 332, 4 - - - 2, 2, 2, 2 - - - 20, 20 - - - 8 - - - Delete this script. - - - btnRemove - - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - $this - - - 1 + 3 357, 6 @@ -386,7 +305,104 @@ $this - 0 + 1 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAERJREFUOE+1zqENADAMA8HOlv3nCXVZSR/ErQxOigJeXpK+4NNxju6WAwNVNZIL + ODIBmktyAUcmQHNJLuC4Aq/wOae1ARVS/s9sUlFoAAAAAElFTkSuQmCC + + + + Zoom + + + NoControl + + + 332, 4 + + + 2, 2, 2, 2 + + + 20, 20 + + + 8 + + + Show menu. + + + btnMenuMore + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + 118, 17 + + + 134, 22 + + + Restart + + + 134, 22 + + + Stop + + + 134, 22 + + + Terminate + + + 122, 22 + + + Action + + + 119, 6 + + + 122, 22 + + + Edit + + + 122, 22 + + + Options + + + 122, 22 + + + Delete + + + 123, 98 + + + contextMenuStripMore + + + System.Windows.Forms.ContextMenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 True @@ -406,6 +422,54 @@ System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + actionToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + restartToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + stopToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + terminateToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripMenuItem1 + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + editToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + removeToolStripMenuItem + + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + LuaUI diff --git a/Plugins/Luna/Views/UserControls/LuaUI.zh-CN.resx b/Plugins/Luna/Views/UserControls/LuaUI.zh-CN.resx index fc6c04a1..18e27338 100644 --- a/Plugins/Luna/Views/UserControls/LuaUI.zh-CN.resx +++ b/Plugins/Luna/Views/UserControls/LuaUI.zh-CN.resx @@ -140,7 +140,51 @@ 停止此脚本 - + + 脚本是否正在运行 + + + 脚本名 + + + (A)自启动 +(C)加载CLR库 +(H)不在托盘菜单显示 +无 - 这不用解释了吧 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAERJREFUOE+1zqENADAMA8HOlv3nCXVZSR/ErQxOigJeXpK+4NNxju6WAwNVNZIL + ODIBmktyAUcmQHNJLuC4Aq/wOae1ARVS/s9sUlFoAAAAAElFTkSuQmCC + + + + 显示菜单 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAJRJREFUOE+tkssNgCAQRE30bls0QBE2YTdeaAQtggNNcF3HZE12Evw7yYTMsm8u + 0IjIJ1eHT8xBVUrp4RFudUQihgKk8JJzFpxTrYQYG7DcwXMIQZxzEmOslliGA4TlIaUk3vvDEmIoqK5K + iKFgdFZCDAWjTwVn8HZPDIUb8CZibMDSL8/4/iPt0pLnX/mNq8P7lmYFTOJvU/foAJoAAAAASUVORK5C + YII= + + + + 重启 + + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + wwAADsMBx2+oZAAAAClJREFUOE9jGGbg27dv/4nBUOWYACS5UI0XLx41YGQYQAyGKh8egIEBAMuiAHSi + jpx4AAAAAElFTkSuQmCC + + + + 停止 + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAAKpJREFUOE/dU0sOQDAU7CEcwIWcgEW3Entns+EIPYUNEolFt/WGJ9GnxGdnklm0 @@ -149,16 +193,28 @@ PGelZoKjbA1QHzFCAAAAAElFTkSuQmCC - - 强制终止此脚本 + + 强制终止 - - 脚本是否正在运行 + + 操作 - - 脚本名 + + + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO + xAAADsQBlSsOGwAAAKhJREFUOE/NkDEOAiEQRfdEHoKeytZTeBIPsFfwCnY2Nh7DxmTX7EA7+kiGiNEN + UEnyMhDmPyYMqpqIMU7AvmlZeHc4KTRLXoEUHLajCZrIAgtTN/ujni9X9d6rc+4nWUCzhZkEaiRZwIFm + QiaokRSCeXqoLFJMYZIQQrp/56sAPiX2NyJSJwAkvGxhapByilUBILnd5zxNswCQcA9dgjX+UNBDFvQT + 9QkHKOQWh3DrDAAAAABJRU5ErkJggg== + + + + 编辑 + + + 选项 - + iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO xAAADsQBlSsOGwAAALZJREFUOE9jQAbfvn0zgDJxApxqgBIVQPwfiDOhQhgAJAdVUwEVggCQwKubV/9v @@ -167,14 +223,11 @@ AZAcVA2qZhgASpCYnRkYAKjTuVoFkX4yAAAAAElFTkSuQmCC - - 删除此脚本 + + 删除 - - (A)自启动 -(C)加载CLR库 -(H)不在托盘菜单显示 -无 - 这不用解释了吧 + + 拖放调整顺序 diff --git a/Plugins/Luna/Views/WinForms/FormChoice.Designer.cs b/Plugins/Luna/Views/WinForms/FormChoice.Designer.cs index 9990b5dd..51c637a6 100644 --- a/Plugins/Luna/Views/WinForms/FormChoice.Designer.cs +++ b/Plugins/Luna/Views/WinForms/FormChoice.Designer.cs @@ -28,49 +28,49 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { - this.components = new System.ComponentModel.Container(); - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormChoice)); - this.btnOk = new System.Windows.Forms.Button(); - this.lbTitle = new System.Windows.Forms.Label(); - this.btnCancel = new System.Windows.Forms.Button(); - this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); - this.SuspendLayout(); - // - // btnOk - // - resources.ApplyResources(this.btnOk, "btnOk"); - this.btnOk.Name = "btnOk"; - this.btnOk.UseVisualStyleBackColor = true; - this.btnOk.Click += new System.EventHandler(this.btnOk_Click); - // - // lbTitle - // - resources.ApplyResources(this.lbTitle, "lbTitle"); - this.lbTitle.Name = "lbTitle"; - // - // btnCancel - // - resources.ApplyResources(this.btnCancel, "btnCancel"); - this.btnCancel.Name = "btnCancel"; - this.btnCancel.UseVisualStyleBackColor = true; - this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); - // - // FormChoice - // - resources.ApplyResources(this, "$this"); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this.lbTitle); - this.Controls.Add(this.btnCancel); - this.Controls.Add(this.btnOk); - this.KeyPreview = true; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "FormChoice"; - this.Load += new System.EventHandler(this.FormChoice_Load); - this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.FormChoice_KeyDown); - this.ResumeLayout(false); - this.PerformLayout(); - + this.components = new System.ComponentModel.Container(); + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FormChoice)); + this.btnOk = new System.Windows.Forms.Button(); + this.lbTitle = new System.Windows.Forms.Label(); + this.btnCancel = new System.Windows.Forms.Button(); + this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); + this.SuspendLayout(); + // + // btnOk + // + resources.ApplyResources(this.btnOk, "btnOk"); + this.btnOk.Name = "btnOk"; + this.btnOk.UseVisualStyleBackColor = true; + this.btnOk.Click += new System.EventHandler(this.btnOk_Click); + // + // lbTitle + // + resources.ApplyResources(this.lbTitle, "lbTitle"); + this.lbTitle.Name = "lbTitle"; + // + // btnCancel + // + resources.ApplyResources(this.btnCancel, "btnCancel"); + this.btnCancel.Name = "btnCancel"; + this.btnCancel.UseVisualStyleBackColor = true; + this.btnCancel.Click += new System.EventHandler(this.btnCancel_Click); + // + // FormChoice + // + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this.lbTitle); + this.Controls.Add(this.btnCancel); + this.Controls.Add(this.btnOk); + this.KeyPreview = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "FormChoice"; + this.Load += new System.EventHandler(this.FormChoice_Load); + this.KeyDown += new System.Windows.Forms.KeyEventHandler(this.FormChoice_KeyDown); + this.ResumeLayout(false); + this.PerformLayout(); + } #endregion diff --git a/Plugins/Luna/Views/WinForms/FormChoice.cs b/Plugins/Luna/Views/WinForms/FormChoice.cs index 7810608b..4fc139b8 100644 --- a/Plugins/Luna/Views/WinForms/FormChoice.cs +++ b/Plugins/Luna/Views/WinForms/FormChoice.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Reflection; using System.Threading; using System.Windows.Forms; @@ -43,6 +44,42 @@ private void FormChoice_Load(object sender, EventArgs e) #endregion #region private methods + void Choose(Keys key) + { + var kts = new Dictionary { + { Keys.D1, 0 }, + { Keys.D2, 1 }, + { Keys.D3, 2 }, + { Keys.D4, 3 }, + { Keys.D5, 4 }, + { Keys.D6, 5 }, + { Keys.D7, 6 }, + { Keys.D8, 7 }, + { Keys.D9, 8 }, + { Keys.D0, 9 }, + }; + + if (!kts.ContainsKey(key)) + { + return; + } + + var idx = kts[key]; + var len = radioButtons.Count; + if (idx >= len) + { + return; + } + + var btn = radioButtons[idx]; + + VgcApis.Misc.UI.Invoke(() => + { + btn.Focus(); + btn.PerformClick(); + }); + } + void SetResult() { for (int i = 0; i < radioButtons.Count; i++) @@ -74,25 +111,40 @@ void InitControls() for (int i = 0; i < num; i++) { - var control = new RadioButton - { - Text = VgcApis.Misc.Utils.AutoEllipsis(choices[i], MAX_CHOICE_LEN), - Left = left, - Top = h * (i + 1) + margin, - AutoSize = true, - }; - - if (defChoice - 1 == i) - { - control.Checked = true; - } - - toolTip1.SetToolTip(control, choices[i]); + RadioButton control = CreateOneChoiceCtrl(margin, left, h, i); Controls.Add(control); radioButtons.Add(control); } } + + private RadioButton CreateOneChoiceCtrl(int margin, int left, int h, int i) + { + var control = new RadioButton + { + Text = VgcApis.Misc.Utils.AutoEllipsis(choices[i], MAX_CHOICE_LEN), + Left = left, + Top = h * (i + 1) + margin, + AutoSize = true, + }; + + if (defChoice - 1 == i) + { + control.Checked = true; + } + + MethodInfo m = typeof(RadioButton).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic); + if (m != null) + { + m.Invoke(control, new object[] { ControlStyles.StandardClick | ControlStyles.StandardDoubleClick, true }); + } + + control.MouseDoubleClick += (s, a) => VgcApis.Misc.UI.Invoke(btnOk.PerformClick); + + toolTip1.SetToolTip(control, choices[i]); + + return control; + } #endregion #region UI event handlers @@ -109,9 +161,18 @@ private void btnCancel_Click(object sender, EventArgs e) private void FormChoice_KeyDown(object sender, KeyEventArgs e) { - if (e.KeyCode == Keys.Escape) + var kc = e.KeyCode; + switch (kc) { - VgcApis.Misc.UI.CloseFormIgnoreError(this); + case Keys.Escape: + VgcApis.Misc.UI.CloseFormIgnoreError(this); + break; + case Keys.Enter: + VgcApis.Misc.UI.Invoke(btnOk.PerformClick); + break; + default: + Choose(kc); + break; } } diff --git a/Plugins/Luna/Views/WinForms/FormEditor.cs b/Plugins/Luna/Views/WinForms/FormEditor.cs index 434c6e8b..2dcc55d2 100644 --- a/Plugins/Luna/Views/WinForms/FormEditor.cs +++ b/Plugins/Luna/Views/WinForms/FormEditor.cs @@ -1,4 +1,5 @@ -using Luna.Resources.Langs; +using Luna.Models.Data; +using Luna.Resources.Langs; using System.Windows.Forms; namespace Luna.Views.WinForms @@ -12,6 +13,7 @@ internal partial class FormEditor : Form Services.LuaServer luaServer; Services.Settings settings; Services.FormMgrSvc formMgr; + private readonly LuaCoreSetting initialCoreSettings; VgcApis.Interfaces.Services.IApiService api; ScintillaNET.Scintilla editor; @@ -21,12 +23,17 @@ public static FormEditor CreateForm( VgcApis.Interfaces.Services.IApiService api, Services.Settings settings, Services.LuaServer luaServer, - Services.FormMgrSvc formMgr) + Services.FormMgrSvc formMgr, + + Models.Data.LuaCoreSetting initialCoreSettings) { FormEditor r = null; VgcApis.Misc.UI.Invoke(() => { - r = new FormEditor(api, settings, luaServer, formMgr); + r = new FormEditor( + api, settings, luaServer, formMgr, + + initialCoreSettings); }); return r; } @@ -35,12 +42,17 @@ public static FormEditor CreateForm( VgcApis.Interfaces.Services.IApiService api, Services.Settings settings, Services.LuaServer luaServer, - Services.FormMgrSvc formMgr) + Services.FormMgrSvc formMgr, + + Models.Data.LuaCoreSetting initialCoreSettings) { this.api = api; this.formMgr = formMgr; + this.initialCoreSettings = initialCoreSettings; + this.settings = settings; this.luaServer = luaServer; + InitializeComponent(); VgcApis.Misc.UI.AutoSetFormIcon(this); title = string.Format(I18N.LunaScrEditor, Properties.Resources.Version); @@ -89,6 +101,7 @@ private void FormEditor_Load(object sender, System.EventArgs e) menuCtrl = new Controllers.FormEditorCtrl.MenuCtrl( this, editorCtrl, + newWindowToolStripMenuItem, showScriptManagerToolStripMenuItem, loadFileToolStripMenuItem, @@ -98,7 +111,7 @@ private void FormEditor_Load(object sender, System.EventArgs e) toolStripStatusClrLib, cboxScriptName); - menuCtrl.Run(formMgr); + menuCtrl.Run(formMgr, initialCoreSettings); this.FormClosing += FormClosingHandler; this.FormClosed += (s, a) => @@ -116,6 +129,8 @@ private void FormEditor_Load(object sender, System.EventArgs e) acmCtrl?.KeyBoardShortcutHandler(a); })); }; + + } #region private methods diff --git a/Plugins/Luna/Views/WinForms/FormMain.Designer.cs b/Plugins/Luna/Views/WinForms/FormMain.Designer.cs index f844019d..04544cfd 100644 --- a/Plugins/Luna/Views/WinForms/FormMain.Designer.cs +++ b/Plugins/Luna/Views/WinForms/FormMain.Designer.cs @@ -62,21 +62,37 @@ private void InitializeComponent() // // toolStripContainer1 // + resources.ApplyResources(this.toolStripContainer1, "toolStripContainer1"); // // toolStripContainer1.BottomToolStripPanel // + resources.ApplyResources(this.toolStripContainer1.BottomToolStripPanel, "toolStripContainer1.BottomToolStripPanel"); this.toolStripContainer1.BottomToolStripPanel.Controls.Add(this.statusStrip1); + this.toolTip1.SetToolTip(this.toolStripContainer1.BottomToolStripPanel, resources.GetString("toolStripContainer1.BottomToolStripPanel.ToolTip")); // // toolStripContainer1.ContentPanel // - this.toolStripContainer1.ContentPanel.Controls.Add(this.panelMainContainer); resources.ApplyResources(this.toolStripContainer1.ContentPanel, "toolStripContainer1.ContentPanel"); - resources.ApplyResources(this.toolStripContainer1, "toolStripContainer1"); + this.toolStripContainer1.ContentPanel.Controls.Add(this.panelMainContainer); + this.toolTip1.SetToolTip(this.toolStripContainer1.ContentPanel, resources.GetString("toolStripContainer1.ContentPanel.ToolTip")); + // + // toolStripContainer1.LeftToolStripPanel + // + resources.ApplyResources(this.toolStripContainer1.LeftToolStripPanel, "toolStripContainer1.LeftToolStripPanel"); + this.toolTip1.SetToolTip(this.toolStripContainer1.LeftToolStripPanel, resources.GetString("toolStripContainer1.LeftToolStripPanel.ToolTip")); this.toolStripContainer1.Name = "toolStripContainer1"; // + // toolStripContainer1.RightToolStripPanel + // + resources.ApplyResources(this.toolStripContainer1.RightToolStripPanel, "toolStripContainer1.RightToolStripPanel"); + this.toolTip1.SetToolTip(this.toolStripContainer1.RightToolStripPanel, resources.GetString("toolStripContainer1.RightToolStripPanel.ToolTip")); + this.toolTip1.SetToolTip(this.toolStripContainer1, resources.GetString("toolStripContainer1.ToolTip")); + // // toolStripContainer1.TopToolStripPanel // + resources.ApplyResources(this.toolStripContainer1.TopToolStripPanel, "toolStripContainer1.TopToolStripPanel"); this.toolStripContainer1.TopToolStripPanel.Controls.Add(this.menuStrip1); + this.toolTip1.SetToolTip(this.toolStripContainer1.TopToolStripPanel, resources.GetString("toolStripContainer1.TopToolStripPanel.ToolTip")); // // statusStrip1 // @@ -85,18 +101,20 @@ private void InitializeComponent() this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.lbStatusBarMsg}); this.statusStrip1.Name = "statusStrip1"; + this.toolTip1.SetToolTip(this.statusStrip1, resources.GetString("statusStrip1.ToolTip")); // // lbStatusBarMsg // - this.lbStatusBarMsg.Name = "lbStatusBarMsg"; resources.ApplyResources(this.lbStatusBarMsg, "lbStatusBarMsg"); + this.lbStatusBarMsg.Name = "lbStatusBarMsg"; // // panelMainContainer // + resources.ApplyResources(this.panelMainContainer, "panelMainContainer"); this.panelMainContainer.Controls.Add(this.panelBtnContainer); this.panelMainContainer.Controls.Add(this.panelFlyContainer); - resources.ApplyResources(this.panelMainContainer, "panelMainContainer"); this.panelMainContainer.Name = "panelMainContainer"; + this.toolTip1.SetToolTip(this.panelMainContainer, resources.GetString("panelMainContainer.ToolTip")); // // panelBtnContainer // @@ -108,6 +126,7 @@ private void InitializeComponent() this.panelBtnContainer.Controls.Add(this.btnOpenEditor); this.panelBtnContainer.Controls.Add(this.btnKillAllScript); this.panelBtnContainer.Name = "panelBtnContainer"; + this.toolTip1.SetToolTip(this.panelBtnContainer, resources.GetString("panelBtnContainer.ToolTip")); // // btnExportToFile // @@ -157,14 +176,16 @@ private void InitializeComponent() resources.ApplyResources(this.panelFlyContainer, "panelFlyContainer"); this.panelFlyContainer.Controls.Add(this.flyScriptUIContainer); this.panelFlyContainer.Name = "panelFlyContainer"; + this.toolTip1.SetToolTip(this.panelFlyContainer, resources.GetString("panelFlyContainer.ToolTip")); // // flyScriptUIContainer // - this.flyScriptUIContainer.AllowDrop = true; resources.ApplyResources(this.flyScriptUIContainer, "flyScriptUIContainer"); + this.flyScriptUIContainer.AllowDrop = true; this.flyScriptUIContainer.BackColor = System.Drawing.SystemColors.Window; this.flyScriptUIContainer.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D; this.flyScriptUIContainer.Name = "flyScriptUIContainer"; + this.toolTip1.SetToolTip(this.flyScriptUIContainer, resources.GetString("flyScriptUIContainer.ToolTip")); this.flyScriptUIContainer.Scroll += new System.Windows.Forms.ScrollEventHandler(this.flyScriptUIContainer_Scroll); // // menuStrip1 @@ -173,31 +194,32 @@ private void InitializeComponent() this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.windowToolStripMenuItem}); this.menuStrip1.Name = "menuStrip1"; + this.toolTip1.SetToolTip(this.menuStrip1, resources.GetString("menuStrip1.ToolTip")); // // windowToolStripMenuItem // + resources.ApplyResources(this.windowToolStripMenuItem, "windowToolStripMenuItem"); this.windowToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { this.showEditorToolStripMenuItem, this.toolStripMenuItem1, this.closeToolStripMenuItem}); this.windowToolStripMenuItem.Name = "windowToolStripMenuItem"; - resources.ApplyResources(this.windowToolStripMenuItem, "windowToolStripMenuItem"); // // showEditorToolStripMenuItem // - this.showEditorToolStripMenuItem.Name = "showEditorToolStripMenuItem"; resources.ApplyResources(this.showEditorToolStripMenuItem, "showEditorToolStripMenuItem"); + this.showEditorToolStripMenuItem.Name = "showEditorToolStripMenuItem"; this.showEditorToolStripMenuItem.Click += new System.EventHandler(this.showEditorToolStripMenuItem_Click); // // toolStripMenuItem1 // - this.toolStripMenuItem1.Name = "toolStripMenuItem1"; resources.ApplyResources(this.toolStripMenuItem1, "toolStripMenuItem1"); + this.toolStripMenuItem1.Name = "toolStripMenuItem1"; // // closeToolStripMenuItem // - this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; resources.ApplyResources(this.closeToolStripMenuItem, "closeToolStripMenuItem"); + this.closeToolStripMenuItem.Name = "closeToolStripMenuItem"; this.closeToolStripMenuItem.Click += new System.EventHandler(this.closeToolStripMenuItem_Click); // // FormMain @@ -208,6 +230,7 @@ private void InitializeComponent() this.KeyPreview = true; this.MainMenuStrip = this.menuStrip1; this.Name = "FormMain"; + this.toolTip1.SetToolTip(this, resources.GetString("$this.ToolTip")); this.Load += new System.EventHandler(this.FormMain_Load); this.toolStripContainer1.BottomToolStripPanel.ResumeLayout(false); this.toolStripContainer1.BottomToolStripPanel.PerformLayout(); diff --git a/Plugins/Luna/Views/WinForms/FormMain.cs b/Plugins/Luna/Views/WinForms/FormMain.cs index dc621a01..7af3f048 100644 --- a/Plugins/Luna/Views/WinForms/FormMain.cs +++ b/Plugins/Luna/Views/WinForms/FormMain.cs @@ -51,9 +51,8 @@ private void FormMain_Load(object sender, System.EventArgs e) btnImportFromFile, btnExportToFile); - genCtrl.Run(luaServer); + genCtrl.Run(luaServer, formMgr); - this.FormClosing += FormClosingHandler; this.FormClosed += (s, a) => { // reverse order @@ -62,13 +61,7 @@ private void FormMain_Load(object sender, System.EventArgs e) } #region private methods - private void FormClosingHandler(object sender, FormClosingEventArgs e) - { - if (settings.IsClosing()) - { - return; - } - } + #endregion #region UI event handlers @@ -88,7 +81,7 @@ private void closeToolStripMenuItem_Click(object sender, System.EventArgs e) } private void btnOpenEditor_Click(object sender, System.EventArgs e) { - formMgr.ShowOrCreateFirstEditor(); + formMgr.CreateNewEditor(); } #endregion diff --git a/Plugins/Luna/Views/WinForms/FormMain.resx b/Plugins/Luna/Views/WinForms/FormMain.resx index f27f5c23..74f7ac2e 100644 --- a/Plugins/Luna/Views/WinForms/FormMain.resx +++ b/Plugins/Luna/Views/WinForms/FormMain.resx @@ -117,568 +117,607 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 17, 17 - - - - None + + System.Windows.Forms.FlowLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 91, 17 + + 0, 0 - - 0 task running + + Terminate all scripts. - - 0, 0 + + 72, 22 - - 520, 22 + + 78, 277 - - - 0 + + - - statusStrip1 + + toolStripContainer1.TopToolStripPanel - - statusStrip1 + + panelFlyContainer - - System.Windows.Forms.StatusStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 3, 3 - - toolStripContainer1.BottomToolStripPanel + + btnStopAllScript - + + btnKillAllScript + + + + + + 0 - - toolStripContainer1.BottomToolStripPanel + + + 2, 2, 2, 2 - - System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 0 - - toolStripContainer1 + + Top, Right - - 4 + + 3, 31 - - Top, Bottom, Right + + panelBtnContainer - - Bottom, Right + + 72, 22 - - NoControl + + 146, 22 - - 3, 252 + + 2, 2, 2, 2 - - 72, 22 + + 268, 268 - - 2 + + Show script editor. - - Export + + Open script editor. - - 183, 17 - - - Export all scripts to file. + + statusStrip1 btnExportToFile - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 520, 25 - - panelBtnContainer + + 91, 17 - + + Export all scripts to file. + + + System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 0 - - Bottom, Right + + Del all - + NoControl + + 0 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + windowToolStripMenuItem + + + System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + toolStripContainer1 + + + True + + + FormMain + + + 3, 3, 3, 3 + + + 72, 22 + 3, 224 - - 72, 22 + + 3, 5 - - 2 + + New - - Import + + 3, 5 - - Import scripts from file. Script with same name will be replaced! + + toolStripContainer1 - - btnImportFromFile + + Show editor - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Fill - - panelBtnContainer + + 0 - - 1 + + - - Bottom, Right + + 0 - - 3, 196 + + - - 72, 22 + + Bottom, Right - - 2 + + 0 task running - - Del all + + 0, 0 - - Delete all scripts. + + 2, 2, 2, 2 - - btnDeleteAllScripts + + panelFlyContainer - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 3 - - panelBtnContainer + + 67, 21 - + 2 - - Top, Right + + 143, 6 - - 3, 31 + + Fill - - 2, 2, 2, 2 + + panelBtnContainer - - 72, 22 + + Top, Bottom, Left, Right - - 0 + + Kill all - - Stop all + + toolStripContainer1 + + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + panelBtnContainer Send stop signal to all scripts. - - btnStopAllScript + + panelMainContainer System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - panelBtnContainer + + Import scripts from file. Script with same name will be replaced! - - 3 + + - - Top, Right + + System.Windows.Forms.ToolStripContainer, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - NoControl + + panelBtnContainer - - 3, 5 + + Delete all scripts. - - 2, 2, 2, 2 + + System.Windows.Forms.StatusStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + toolStripContainer1.BottomToolStripPanel + + 72, 22 - - 1 + + 520, 283 - - Editor + + 4 - - Open script editor. + + statusStrip1 - - btnOpenEditor + + 2, 2, 2, 2 - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + toolStripMenuItem1 - - panelBtnContainer + + flyScriptUIContainer + + + 146, 22 + + + 0 4 - - Top, Right + + FormMain - - 3, 57 + + 2 - - 2, 2, 2, 2 + + 1 - - 72, 22 + + NoControl - - 1 + + 0 - - Kill all + + closeToolStripMenuItem - - Terminate all scripts. + + toolStripContainer1.BottomToolStripPanel - - btnKillAllScript + + 0 - - System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - panelBtnContainer + + toolStripContainer1.LeftToolStripPanel - - 5 + + 2, 2, 2, 2 - - 439, 3 + + panelMainContainer - - 78, 277 + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + + 0, 0 + + 1 - - panelBtnContainer + + 72, 22 - - System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + CenterScreen - - panelMainContainer + + 3, 3, 3, 3 - - 0 + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - Top, Bottom, Left, Right + + panelMainContainer - - True + + showEditorToolStripMenuItem - - Fill + + System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 3, 3 + + 0 - - 2, 2, 2, 2 + + 428, 269 - - 0 - - - flyScriptUIContainer - - - System.Windows.Forms.FlowLayoutPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + toolStripContainer1.ContentPanel - - panelFlyContainer + + Close - - 0 + + None - - 3, 5 + + 1 - - 3, 3, 3, 3 + + 1 434, 275 - - 1 + + NoControl - - panelFlyContainer + + None - - System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + - - panelMainContainer + + 0, 0 - - 1 + + 520, 283 Fill - - 0, 0 + + - - 3, 3, 3, 3 + + 1 - - 520, 283 + + 1 - - 2 + + panelBtnContainer - - panelMainContainer + + Import - - System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 439, 3 - - toolStripContainer1.ContentPanel + + toolStripContainer1.RightToolStripPanel - - 0 + + toolTip1 - - 2, 2, 2, 2 + + 72, 22 - - 520, 283 + + 2 - - toolStripContainer1.ContentPanel + + $this - - System.Windows.Forms.ToolStripContentPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - toolStripContainer1 + + Stop all - + + 3, 196 + + 0 - - Fill + + System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - toolStripContainer1.LeftToolStripPanel + + toolStripContainer1.TopToolStripPanel - - System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - toolStripContainer1 + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 1 + + System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 0, 0 + + btnImportFromFile - + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 2, 2, 2, 2 - - toolStripContainer1.RightToolStripPanel + + btnOpenEditor - - System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 1 - + + 0 + + + Bottom, Right + + toolStripContainer1 + + 1 + 2 - - 520, 330 + + System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 1 + + - - toolStripContainer1 + + lbStatusBarMsg - - 284, 17 - - - None + + 2 - - 146, 22 + + Top, Right - - Show editor + + 6, 12 - - Show script editor. + + 520, 22 - - 143, 6 + + Export - - 146, 22 + + toolStripContainer1.ContentPanel - - Close + + - - Close this window. + + 2, 2, 2, 2 - - 67, 21 + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 Window - - 0, 0 - - - 520, 25 + + 3 - - 0 + + toolStripContainer1 menuStrip1 - - menuStrip1 - - - System.Windows.Forms.MenuStrip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - toolStripContainer1.TopToolStripPanel - - - 0 + + 3, 252 - - toolStripContainer1.TopToolStripPanel + + 520, 330 - - System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + panelBtnContainer - - toolStripContainer1 + + - - 3 + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + toolStripContainer1 - - System.Windows.Forms.ToolStripContainer, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.Panel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - $this + + System.Windows.Forms.ToolStripPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - 0 + + Close this window. - - True - - - 6, 12 + + 2 - + 520, 330 - - 2, 2, 2, 2 - - - 268, 268 - - - CenterScreen - - - FormMain - - - lbStatusBarMsg - - - System.Windows.Forms.ToolStripStatusLabel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + - - windowToolStripMenuItem + + panelBtnContainer - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Top, Bottom, Right - - showEditorToolStripMenuItem + + 5 - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + System.Windows.Forms.ToolStripContentPanel, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - toolStripMenuItem1 + + menuStrip1 - - System.Windows.Forms.ToolStripSeparator, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + toolStripContainer1 - - closeToolStripMenuItem + + Top, Right - - System.Windows.Forms.ToolStripMenuItem, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + btnDeleteAllScripts - - toolTip1 + + System.Windows.Forms.Button, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - System.Windows.Forms.ToolTip, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + 3, 57 - - FormMain + + - - System.Windows.Forms.Form, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + Bottom, Right + + True + + + 284, 17 + + + 17, 17 + + + 183, 17 + \ No newline at end of file diff --git a/Plugins/Luna/Views/WinForms/FormMain.zh-CN.resx b/Plugins/Luna/Views/WinForms/FormMain.zh-CN.resx index 179c7289..1b8ab8ef 100644 --- a/Plugins/Luna/Views/WinForms/FormMain.zh-CN.resx +++ b/Plugins/Luna/Views/WinForms/FormMain.zh-CN.resx @@ -117,6 +117,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + + + 导出... @@ -142,7 +148,7 @@ 给所有脚本发送停止信号 - 编辑器 + 新脚本 打开脚本编辑器 @@ -153,8 +159,33 @@ 强制停止所有脚本 - - 窗口 + + + + + + + + + + + + + + + + + + + + + + + + + + + 180, 22 脚本编辑器 @@ -162,10 +193,31 @@ 打开脚本编辑器 + + 177, 6 + + + 180, 22 + 关闭窗口 关闭这个窗口 + + 44, 21 + + + 窗口 + + + + + + + + + + \ No newline at end of file diff --git a/Plugins/ProxySetter/Services/PsLuncher.cs b/Plugins/ProxySetter/Services/PsLuncher.cs index 9cb4c947..4bec6c0f 100644 --- a/Plugins/ProxySetter/Services/PsLuncher.cs +++ b/Plugins/ProxySetter/Services/PsLuncher.cs @@ -61,12 +61,10 @@ public void Show() public void Cleanup() { FileLogger.Info("ProxySetting.Cleanup() begin"); + setting.SetIsDisposing(true); setting.DebugLog("call Luncher.cleanup"); serverTracker.OnSysProxyChanged -= UpdateMenuItemCheckedStatHandler; - if (!setting.IsClosing()) - { - VgcApis.Misc.UI.CloseFormIgnoreError(formMain); - } + VgcApis.Misc.UI.CloseFormIgnoreError(formMain); serverTracker.Cleanup(); pacServer.Cleanup(); setting.Cleanup(); diff --git a/Plugins/ProxySetter/Services/PsSettings.cs b/Plugins/ProxySetter/Services/PsSettings.cs index 5e04318d..57bc2043 100644 --- a/Plugins/ProxySetter/Services/PsSettings.cs +++ b/Plugins/ProxySetter/Services/PsSettings.cs @@ -17,7 +17,10 @@ public void Run( #region public methods - public bool IsClosing() => setting.IsClosing(); + bool _IsDisposing = false; + public void SetIsDisposing(bool value) => _IsDisposing = value; + + public bool IsClosing() => _IsDisposing || setting.IsClosing(); public void DebugLog(string content) { #if DEBUG diff --git a/README.md b/README.md index ae5a913f..e02903ad 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ Require .net framework 4.5+. ### 引用按字母排序 Credits (in alphabetical order) [2dust/v2rayN](https://github.com/2dust/v2rayN) vmess分享链接及订阅格式 [Ahmad45123/AutoCompleteMenu-ScintillaNET](https://github.com/Ahmad45123/AutoCompleteMenu-ScintillaNET) 自动补全 +[brunoos/luasec](https://github.com/brunoos/luasec.git) Lua https +[diegonehab/luasocket](https://github.com/diegonehab/luasocket.git) Lua socket [FourierTransformer/lua-complete](https://github.com/FourierTransformer/lua-complete.git) lua模块补全 [haf/DotNetZip.Semverd](https://github.com/haf/DotNetZip.Semverd) .net 4.0解压zip文件 [HtmlAgilityPack](https://html-agility-pack.net/) HTML解释器 diff --git a/V2RayGCon/Properties/AssemblyInfo.cs b/V2RayGCon/Properties/AssemblyInfo.cs index fffde3de..ad3947b5 100644 --- a/V2RayGCon/Properties/AssemblyInfo.cs +++ b/V2RayGCon/Properties/AssemblyInfo.cs @@ -33,10 +33,23 @@ // 可以指定所有值,也可以使用以下所示的 "*" 预置版本号和修订号 // 方法是按如下所示使用“*”: : // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.3.9.0")] +[assembly: AssemblyVersion("1.4.0.0")] [assembly: AssemblyFileVersion("1.0.0.0")] /* + * ---------------------------------------- + * v1.3.9.8 Fix a bug which would block core updating. + * v1.3.9.7 Add luasocket and luasec. + * v1.3.9.6 Add Sys:CreateHttpServer(). + * v1.3.9.5 Adjust LuaUI. + * v1.3.9.4 Selection hotkeys support in Misc:Choice(). + * Add feature, stop script from systray menu. + * Add Sys:Volume*(). + * Fix index out of range again. + * v1.3.9.3 Add edit button in LuaUI. + * v1.3.9.2 Fix index out of range bug. + * Choice support double click. + * v1.3.9.1 Fix bugs in analyzer. * ----------------------------------------------------- * v1.3.8.5 Tweak serverUI labels' color. * v1.3.8.4 Fix a bug in modules.set.lua. diff --git a/V2RayGCon/Resources/Files/lua/dll64/mime/core.dll b/V2RayGCon/Resources/Files/lua/dll64/mime/core.dll new file mode 100644 index 00000000..4fa8f232 Binary files /dev/null and b/V2RayGCon/Resources/Files/lua/dll64/mime/core.dll differ diff --git a/V2RayGCon/Resources/Files/lua/dll64/socket/core.dll b/V2RayGCon/Resources/Files/lua/dll64/socket/core.dll new file mode 100644 index 00000000..c30be66e Binary files /dev/null and b/V2RayGCon/Resources/Files/lua/dll64/socket/core.dll differ diff --git a/V2RayGCon/Resources/Files/lua/dll64/ssl.dll b/V2RayGCon/Resources/Files/lua/dll64/ssl.dll new file mode 100644 index 00000000..e80a00ab Binary files /dev/null and b/V2RayGCon/Resources/Files/lua/dll64/ssl.dll differ diff --git a/V2RayGCon/Resources/Files/lua/libs/bencode.lua b/V2RayGCon/Resources/Files/lua/libs/bencode.lua new file mode 100644 index 00000000..b2cbac31 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/bencode.lua @@ -0,0 +1,186 @@ +--[[ + Lua module for handling bencoded data as used by bittorrent. + + This module includes both a recursive decoder and a recursive encoder. + + See the file COPYING included with the lua-bencode distribution for + details on copyright holders and the terms and conditions which apply + when copying this file. + +]]-- + +local sort, concat, insert = table.sort, table.concat, table.insert +local pairs, ipairs, type, tonumber = pairs, ipairs, type, tonumber +local sub, find = string.sub, string.find + +local M = {} + +-- helpers + +local function islist(t) + local n = #t + for k, v in pairs(t) do + if type(k) ~= "number" + or k % 1 ~= 0 -- integer? + or k < 1 + or k > n + then + return false + end + end + for i = 1, n do + if t[i] == nil then + return false + end + end + return true +end + +-- encoder functions + +local encode_rec -- encode_list/dict and encode_rec are mutually recursive... + +local function encode_list(t, x) + + insert(t, "l") + + for _,v in ipairs(x) do + local err,ev = encode_rec(t, v); if err then return err,ev end + end + + insert(t, "e") +end + +local function encode_dict(t, x) + insert(t, "d") + -- bittorrent requires the keys to be sorted. + local sortedkeys = {} + for k, v in pairs(x) do + if type(k) ~= "string" then + return "bencoding requires dictionary keys to be strings", k + end + insert(sortedkeys, k) + end + sort(sortedkeys) + + for k, v in ipairs(sortedkeys) do + local err,ev = encode_rec(t, v); if err then return err,ev end + err,ev = encode_rec(t, x[v]); if err then return err,ev end + end + insert(t, "e") +end + +local function encode_int(t, x) + + if x % 1 ~= 0 then return "number is not an integer", x end + insert(t, "i" ) + insert(t, x ) + insert(t, "e" ) +end + +local function encode_str(t, x) + + insert(t, #x ) + insert(t, ":" ) + insert(t, x ) +end + +encode_rec = function(t, x) + + local typx = type(x) + if typx == "string" then return encode_str (t, x) + elseif typx == "number" then return encode_int (t, x) + elseif typx == "table" then + + if islist(x) then return encode_list (t, x) + else return encode_dict (t, x) + end + else + return "type cannot be converted to an acceptable type for bencoding", typx + end +end + +-- call recursive bencoder function with empty table, stringify that table. +-- this is the only encode* function visible to module users. +M.encode = function (x) + + local t = {} + local err, val = encode_rec(t,x) + if not err then + return concat(t) + else + return nil, err, val + end +end + +-- decoder functions + +local function decode_integer(s, index) + local a, b, int = find(s, "^(%-?%d+)e", index) + if not int then return nil, "not a number", nil end + int = tonumber(int) + if not int then return nil, "not a number", int end + return int, b + 1 +end + +local function decode_list(s, index) + local t = {} + while sub(s, index, index) ~= "e" do + local obj, ev + obj, index, ev = M.decode(s, index) + if not obj then return obj, index, ev end + insert(t, obj) + end + index = index + 1 + return t, index +end + +local function decode_dictionary(s, index) + local t = {} + while sub(s, index, index) ~= "e" do + local obj1, obj2, ev + + obj1, index, ev = M.decode(s, index) + if not obj1 then return obj1, index, ev end + + obj2, index, ev = M.decode(s, index) + if not obj2 then return obj2, index, ev end + + t[obj1] = obj2 + end + index = index + 1 + return t, index +end + +local function decode_string(s, index) + local a, b, len = find(s, "^([0-9]+):", index) + if not len then return nil, "not a length", len end + index = b + 1 + + local v = sub(s, index, index + len - 1) + if #v < tonumber(len) then return nil, "truncated string at end of input", v end + index = index + len + return v, index +end + + +M.decode = function (s, index) + if not s then return nil, "no data", nil end + index = index or 1 + local t = sub(s, index, index) + if not t then return nil, "truncation error", nil end + + if t == "i" then + return decode_integer(s, index + 1) + elseif t == "l" then + return decode_list(s, index + 1) + elseif t == "d" then + return decode_dictionary(s, index + 1) + elseif t >= '0' and t <= '9' then + return decode_string(s, index) + else + return nil, "invalid type", t + end +end + +return M diff --git a/V2RayGCon/Resources/Files/lua/libs/luacheck/analyzer.lua b/V2RayGCon/Resources/Files/lua/libs/luacheck/analyzer.lua index d1cd7fa0..38049659 100644 --- a/V2RayGCon/Resources/Files/lua/libs/luacheck/analyzer.lua +++ b/V2RayGCon/Resources/Files/lua/libs/luacheck/analyzer.lua @@ -14,6 +14,26 @@ local anz = {} local function DumpTable(t) print( table.dump(t, " ", " >(((@> ") ) end + +local function ParseVarName(name) + + if type(name) == "string" + and string.match(name, "[^%w^_]") == nil + then + if string.match(name, "[^%d^.]") == nil then + -- pure numbers + return "[" .. name .. "]" + else + return name + end + end + + local s = tostring(name) + if string.match(s, '"') ~= nil then + return "['" .. s .. "']" + end + return '["' .. s .. '"]' +end local function InsertOnce(t, v) if not table.contains(t, v) then @@ -21,7 +41,6 @@ local function InsertOnce(t, v) end end - local function analyzeFuncParams(f) local ft = "funcs" @@ -224,18 +243,22 @@ end -- anz codes start local function analyzeName(n, sep) + if type(n) ~= "table" then - return false, n + return false, ParseVarName(n) end - if n.tag == "Id" then - return false, n[1] - elseif n.tag == "Index" and n[1] and n[1][1] and n[2] then - return true, n[1][1] .. sep .. n[2][1] + if n.tag == "Index" and n[1] and n[2] then + local itl, left = analyzeName(n[1], ".") + local itr, right = analyzeName(n[2], ".") + local c = "." + if right ~= nil and string.sub(right, 1, 1) == '[' then + c = "" + end + return true, left .. c .. right end - - -- unknow error - return false, "" + + return false, ParseVarName(n[1]) end local function analyzeRequirePath(v, r) @@ -265,7 +288,7 @@ local function analyzeRequirePath(v, r) return "", "" end -local function anlyzeValue(v, r) +local function analyzeValue(v, r) if type(v) ~= "table" then return "vars", nil end @@ -309,7 +332,7 @@ end local function analyzeEqual(n, v, r, ln) - local vt, vv = anlyzeValue(v, r) + local vt, vv = analyzeValue(v, r) -- print( table.dump(v) ) -- print("vt: ", vt) @@ -353,16 +376,19 @@ end local function analyzeLines(ast, r) for n, line in ipairs(ast) do - local ln = line["line"] - local len = table.length(line[1]) - for n = 1, len do - if line[2] and line[2][n] then - -- print( "analyzing: ", table.dump(line)) - analyzeEqual(line[1][n], line[2][n], r, ln) - else - local isTable, name = analyzeName(line[1][n], ".") - if name ~= nil and name ~= "" and r["vars"][name] == nil then - r["vars"][name] = ln + local tag = line.tag + if tag == "Local" or tag == "Set" or tag == "Localrec" then + local ln = line["line"] + local len = table.length(line[1]) + for n = 1, len do + if line[2] and line[2][n] then + -- print( "analyzing: ", table.dump(line)) + analyzeEqual(line[1][n], line[2][n], r, ln) + else + local isTable, name = analyzeName(line[1][n], ".") + if name ~= nil and name ~= "" and r["vars"][name] == nil then + r["vars"][name] = ln + end end end end @@ -423,6 +449,14 @@ local function Add(a, b) return c end +function t.abc(self, a, b) + print(a, b) +end + +t['{'] = function(a, b) + return a +end + function Main(ps) print("hello!") return true @@ -495,8 +529,6 @@ local function testAnalyzeModuleEx() print(r) end - - -- testCodeToAst(testCode) -- testAnalyzeCode() -- testAnalyzeModule() @@ -510,4 +542,4 @@ function anz.new() return o end -return anz \ No newline at end of file +return anz diff --git a/V2RayGCon/Resources/Files/lua/libs/luasec/https.lua b/V2RayGCon/Resources/Files/lua/libs/luasec/https.lua new file mode 100644 index 00000000..864ba927 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasec/https.lua @@ -0,0 +1,146 @@ +---------------------------------------------------------------------------- +-- LuaSec 0.9 +-- Copyright (C) 2009-2019 PUC-Rio +-- +-- Author: Pablo Musa +-- Author: Tomas Guisasola +--------------------------------------------------------------------------- + +local socket = require('lua.libs.luasocket.socket') +local ssl = require('lua.libs.luasec.ssl') +local ltn12 = require('lua.libs.luasocket.ltn12') +local http = require('lua.libs.luasocket.socket.http') +local url = require('lua.libs.luasocket.socket.url') + +local try = socket.try + +-- +-- Module +-- +local _M = { + _VERSION = "0.9", + _COPYRIGHT = "LuaSec 0.9 - Copyright (C) 2009-2019 PUC-Rio", + PORT = 443, + TIMEOUT = 60 +} + +-- TLS configuration +local cfg = { + protocol = "any", + options = {"all", "no_sslv2", "no_sslv3", "no_tlsv1"}, + verify = "none", +} + +-------------------------------------------------------------------- +-- Auxiliar Functions +-------------------------------------------------------------------- + +-- Insert default HTTPS port. +local function default_https_port(u) + return url.build(url.parse(u, {port = _M.PORT})) +end + +-- Convert an URL to a table according to Luasocket needs. +local function urlstring_totable(url, body, result_table) + url = { + url = default_https_port(url), + method = body and "POST" or "GET", + sink = ltn12.sink.table(result_table) + } + if body then + url.source = ltn12.source.string(body) + url.headers = { + ["content-length"] = #body, + ["content-type"] = "application/x-www-form-urlencoded", + } + end + return url +end + +-- Forward calls to the real connection object. +local function reg(conn) + local mt = getmetatable(conn.sock).__index + for name, method in pairs(mt) do + if type(method) == "function" then + conn[name] = function (self, ...) + return method(self.sock, ...) + end + end + end +end + +-- Return a function which performs the SSL/TLS connection. +local function tcp(params) + params = params or {} + -- Default settings + for k, v in pairs(cfg) do + params[k] = params[k] or v + end + -- Force client mode + params.mode = "client" + -- 'create' function for LuaSocket + return function () + local conn = {} + conn.sock = try(socket.tcp()) + local st = getmetatable(conn.sock).__index.settimeout + function conn:settimeout(...) + return st(self.sock, _M.TIMEOUT) + end + -- Replace TCP's connection function + function conn:connect(host, port) + try(self.sock:connect(host, port)) + self.sock = try(ssl.wrap(self.sock, params)) + self.sock:sni(host) + self.sock:settimeout(_M.TIMEOUT) + try(self.sock:dohandshake()) + reg(self, getmetatable(self.sock)) + return 1 + end + return conn + end +end + +-------------------------------------------------------------------- +-- Main Function +-------------------------------------------------------------------- + +-- Make a HTTP request over secure connection. This function receives +-- the same parameters of LuaSocket's HTTP module (except 'proxy' and +-- 'redirect') plus LuaSec parameters. +-- +-- @param url mandatory (string or table) +-- @param body optional (string) +-- @return (string if url == string or 1), code, headers, status +-- +local function request(url, body) + local result_table = {} + local stringrequest = type(url) == "string" + if stringrequest then + url = urlstring_totable(url, body, result_table) + else + url.url = default_https_port(url.url) + end + if http.PROXY or url.proxy then + return nil, "proxy not supported" + elseif url.redirect then + return nil, "redirect not supported" + elseif url.create then + return nil, "create function not permitted" + end + -- New 'create' function to establish a secure connection + url.create = tcp(url) + local res, code, headers, status = http.request(url) + if res and stringrequest then + return table.concat(result_table), code, headers, status + end + return res, code, headers, status +end + +-------------------------------------------------------------------------------- +-- Export module +-- + +_M.request = request +_M.tcp = tcp + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasec/options.lua b/V2RayGCon/Resources/Files/lua/libs/luasec/options.lua new file mode 100644 index 00000000..3915d92f --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasec/options.lua @@ -0,0 +1,90 @@ +local function usage() + print("Usage:") + print("* Generate options of your system:") + print(" lua options.lua -g /path/to/ssl.h [version] > options.c") + print("* Examples:") + print(" lua options.lua -g /usr/include/openssl/ssl.h > options.c\n") + print(" lua options.lua -g /usr/include/openssl/ssl.h \"OpenSSL 1.0.1 14\" > options.c\n") + + print("* List options of your system:") + print(" lua options.lua -l /path/to/ssl.h\n") +end + +-- +local function printf(str, ...) + print(string.format(str, ...)) +end + +local function generate(options, version) + print([[ +/*-------------------------------------------------------------------------- + * LuaSec 0.9 + * + * Copyright (C) 2006-2019 Bruno Silvestre + * + *--------------------------------------------------------------------------*/ + +#include + +#include "options.h" + +/* If you need to generate these options again, see options.lua */ + +]]) + + printf([[ +/* + OpenSSL version: %s +*/ +]], version) + + print([[static lsec_ssl_option_t ssl_options[] = {]]) + + for k, option in ipairs(options) do + local name = string.lower(string.sub(option, 8)) + print(string.format([[#if defined(%s)]], option)) + print(string.format([[ {"%s", %s},]], name, option)) + print([[#endif]]) + end + print([[ {NULL, 0L}]]) + print([[ +}; + +LSEC_API lsec_ssl_option_t* lsec_get_ssl_options() { + return ssl_options; +} +]]) +end + +local function loadoptions(file) + local options = {} + local f = assert(io.open(file, "r")) + for line in f:lines() do + local op = string.match(line, "define%s+(SSL_OP_%S+)") + if op then + table.insert(options, op) + end + end + table.sort(options, function(a,b) return a 255 then + return nil, "invalid ALPN name (length > 255)" + end + str = str .. string.char(len) .. v + end + if str == "" then return nil, "invalid ALPN list (empty)" end + return str +end + +-- +-- Convert wire-string format to array +-- +local function wireformat2array(str) + local i = 1 + local array = {} + while i < #str do + local len = str:byte(i) + array[#array + 1] = str:sub(i + 1, i + len) + i = i + len + 1 + end + return array +end + +-- +-- +-- +local function newcontext(cfg) + local succ, msg, ctx + -- Create the context + ctx, msg = context.create(cfg.protocol) + if not ctx then return nil, msg end + -- Mode + succ, msg = context.setmode(ctx, cfg.mode) + if not succ then return nil, msg end + local certificates = cfg.certificates + if not certificates then + certificates = { + { certificate = cfg.certificate, key = cfg.key, password = cfg.password } + } + end + for _, certificate in ipairs(certificates) do + -- Load the key + if certificate.key then + if certificate.password and + type(certificate.password) ~= "function" and + type(certificate.password) ~= "string" + then + return nil, "invalid password type" + end + succ, msg = context.loadkey(ctx, certificate.key, certificate.password) + if not succ then return nil, msg end + end + -- Load the certificate(s) + if certificate.certificate then + succ, msg = context.loadcert(ctx, certificate.certificate) + if not succ then return nil, msg end + if certificate.key and context.checkkey then + succ = context.checkkey(ctx) + if not succ then return nil, "private key does not match public key" end + end + end + end + -- Load the CA certificates + if cfg.cafile or cfg.capath then + succ, msg = context.locations(ctx, cfg.cafile, cfg.capath) + if not succ then return nil, msg end + end + -- Set SSL ciphers + if cfg.ciphers then + succ, msg = context.setcipher(ctx, cfg.ciphers) + if not succ then return nil, msg end + end + -- Set SSL cipher suites + if cfg.ciphersuites then + succ, msg = context.setciphersuites(ctx, cfg.ciphersuites) + if not succ then return nil, msg end + end + -- Set the verification options + succ, msg = optexec(context.setverify, cfg.verify, ctx) + if not succ then return nil, msg end + -- Set SSL options + succ, msg = optexec(context.setoptions, cfg.options, ctx) + if not succ then return nil, msg end + -- Set the depth for certificate verification + if cfg.depth then + succ, msg = context.setdepth(ctx, cfg.depth) + if not succ then return nil, msg end + end + + -- NOTE: Setting DH parameters and elliptic curves needs to come after + -- setoptions(), in case the user has specified the single_{dh,ecdh}_use + -- options. + + -- Set DH parameters + if cfg.dhparam then + if type(cfg.dhparam) ~= "function" then + return nil, "invalid DH parameter type" + end + context.setdhparam(ctx, cfg.dhparam) + end + + -- Set elliptic curves + if (not config.algorithms.ec) and (cfg.curve or cfg.curveslist) then + return false, "elliptic curves not supported" + end + if config.capabilities.curves_list and cfg.curveslist then + succ, msg = context.setcurveslist(ctx, cfg.curveslist) + if not succ then return nil, msg end + elseif cfg.curve then + succ, msg = context.setcurve(ctx, cfg.curve) + if not succ then return nil, msg end + end + + -- Set extra verification options + if cfg.verifyext and ctx.setverifyext then + succ, msg = optexec(ctx.setverifyext, cfg.verifyext, ctx) + if not succ then return nil, msg end + end + + -- ALPN + if cfg.mode == "server" and cfg.alpn then + if type(cfg.alpn) == "function" then + local alpncb = cfg.alpn + -- This callback function has to return one value only + succ, msg = context.setalpncb(ctx, function(str) + local protocols = alpncb(wireformat2array(str)) + if type(protocols) == "string" then + protocols = { protocols } + elseif type(protocols) ~= "table" then + return nil + end + return (array2wireformat(protocols)) -- use "()" to drop error message + end) + if not succ then return nil, msg end + elseif type(cfg.alpn) == "table" then + local protocols = cfg.alpn + -- check if array is valid before use it + succ, msg = array2wireformat(protocols) + if not succ then return nil, msg end + -- This callback function has to return one value only + succ, msg = context.setalpncb(ctx, function() + return (array2wireformat(protocols)) -- use "()" to drop error message + end) + if not succ then return nil, msg end + else + return nil, "invalid ALPN parameter" + end + elseif cfg.mode == "client" and cfg.alpn then + local alpn + if type(cfg.alpn) == "string" then + alpn, msg = array2wireformat({ cfg.alpn }) + elseif type(cfg.alpn) == "table" then + alpn, msg = array2wireformat(cfg.alpn) + else + return nil, "invalid ALPN parameter" + end + if not alpn then return nil, msg end + succ, msg = context.setalpn(ctx, alpn) + if not succ then return nil, msg end + end + + if config.capabilities.dane and cfg.dane then + context.setdane(ctx) + end + + return ctx +end + +-- +-- +-- +local function wrap(sock, cfg) + local ctx, msg + if type(cfg) == "table" then + ctx, msg = newcontext(cfg) + if not ctx then return nil, msg end + else + ctx = cfg + end + local s, msg = core.create(ctx) + if s then + core.setfd(s, sock:getfd()) + sock:setfd(core.SOCKET_INVALID) + registry[s] = ctx + return s + end + return nil, msg +end + +-- +-- Extract connection information. +-- +local function info(ssl, field) + local str, comp, err, protocol + comp, err = core.compression(ssl) + if err then + return comp, err + end + -- Avoid parser + if field == "compression" then + return comp + end + local info = {compression = comp} + str, info.bits, info.algbits, protocol = core.info(ssl) + if str then + info.cipher, info.protocol, info.key, + info.authentication, info.encryption, info.mac = + string.match(str, + "^(%S+)%s+(%S+)%s+Kx=(%S+)%s+Au=(%S+)%s+Enc=(%S+)%s+Mac=(%S+)") + info.export = (string.match(str, "%sexport%s*$") ~= nil) + end + if protocol then + info.protocol = protocol + end + if field then + return info[field] + end + -- Empty? + return ( (next(info)) and info ) +end + +-- +-- Set method for SSL connections. +-- +core.setmethod("info", info) + +-------------------------------------------------------------------------------- +-- Export module +-- + +local _M = { + _VERSION = "0.9", + _COPYRIGHT = core.copyright(), + config = config, + loadcertificate = x509.load, + newcontext = newcontext, + wrap = wrap, +} + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/ltn12.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/ltn12.lua new file mode 100644 index 00000000..afa735dc --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/ltn12.lua @@ -0,0 +1,319 @@ +----------------------------------------------------------------------------- +-- LTN12 - Filters, sources, sinks and pumps. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local table = require("table") +local unpack = unpack or table.unpack +local base = _G +local _M = {} +if module then -- heuristic for exporting a global package table + ltn12 = _M +end +local filter,source,sink,pump = {},{},{},{} + +_M.filter = filter +_M.source = source +_M.sink = sink +_M.pump = pump + +local unpack = unpack or table.unpack +local select = base.select + +-- 2048 seems to be better in windows... +_M.BLOCKSIZE = 2048 +_M._VERSION = "LTN12 1.0.3" + +----------------------------------------------------------------------------- +-- Filter stuff +----------------------------------------------------------------------------- +-- returns a high level filter that cycles a low-level filter +function filter.cycle(low, ctx, extra) + base.assert(low) + return function(chunk) + local ret + ret, ctx = low(ctx, chunk, extra) + return ret + end +end + +-- chains a bunch of filters together +-- (thanks to Wim Couwenberg) +function filter.chain(...) + local arg = {...} + local n = base.select('#',...) + local top, index = 1, 1 + local retry = "" + return function(chunk) + retry = chunk and retry + while true do + if index == top then + chunk = arg[index](chunk) + if chunk == "" or top == n then return chunk + elseif chunk then index = index + 1 + else + top = top+1 + index = top + end + else + chunk = arg[index](chunk or "") + if chunk == "" then + index = index - 1 + chunk = retry + elseif chunk then + if index == n then return chunk + else index = index + 1 end + else base.error("filter returned inappropriate nil") end + end + end + end +end + +----------------------------------------------------------------------------- +-- Source stuff +----------------------------------------------------------------------------- +-- create an empty source +local function empty() + return nil +end + +function source.empty() + return empty +end + +-- returns a source that just outputs an error +function source.error(err) + return function() + return nil, err + end +end + +-- creates a file source +function source.file(handle, io_err) + if handle then + return function() + local chunk = handle:read(_M.BLOCKSIZE) + if not chunk then handle:close() end + return chunk + end + else return source.error(io_err or "unable to open file") end +end + +-- turns a fancy source into a simple source +function source.simplify(src) + base.assert(src) + return function() + local chunk, err_or_new = src() + src = err_or_new or src + if not chunk then return nil, err_or_new + else return chunk end + end +end + +-- creates string source +function source.string(s) + if s then + local i = 1 + return function() + local chunk = string.sub(s, i, i+_M.BLOCKSIZE-1) + i = i + _M.BLOCKSIZE + if chunk ~= "" then return chunk + else return nil end + end + else return source.empty() end +end + +-- creates table source +function source.table(t) + base.assert('table' == type(t)) + local i = 0 + return function() + i = i + 1 + return t[i] + end +end + +-- creates rewindable source +function source.rewind(src) + base.assert(src) + local t = {} + return function(chunk) + if not chunk then + chunk = table.remove(t) + if not chunk then return src() + else return chunk end + else + table.insert(t, chunk) + end + end +end + +-- chains a source with one or several filter(s) +function source.chain(src, f, ...) + if ... then f=filter.chain(f, ...) end + base.assert(src and f) + local last_in, last_out = "", "" + local state = "feeding" + local err + return function() + if not last_out then + base.error('source is empty!', 2) + end + while true do + if state == "feeding" then + last_in, err = src() + if err then return nil, err end + last_out = f(last_in) + if not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + elseif last_out ~= "" then + state = "eating" + if last_in then last_in = "" end + return last_out + end + else + last_out = f(last_in) + if last_out == "" then + if last_in == "" then + state = "feeding" + else + base.error('filter returned ""') + end + elseif not last_out then + if last_in then + base.error('filter returned inappropriate nil') + else + return nil + end + else + return last_out + end + end + end + end +end + +-- creates a source that produces contents of several sources, one after the +-- other, as if they were concatenated +-- (thanks to Wim Couwenberg) +function source.cat(...) + local arg = {...} + local src = table.remove(arg, 1) + return function() + while src do + local chunk, err = src() + if chunk then return chunk end + if err then return nil, err end + src = table.remove(arg, 1) + end + end +end + +----------------------------------------------------------------------------- +-- Sink stuff +----------------------------------------------------------------------------- +-- creates a sink that stores into a table +function sink.table(t) + t = t or {} + local f = function(chunk, err) + if chunk then table.insert(t, chunk) end + return 1 + end + return f, t +end + +-- turns a fancy sink into a simple sink +function sink.simplify(snk) + base.assert(snk) + return function(chunk, err) + local ret, err_or_new = snk(chunk, err) + if not ret then return nil, err_or_new end + snk = err_or_new or snk + return 1 + end +end + +-- creates a file sink +function sink.file(handle, io_err) + if handle then + return function(chunk, err) + if not chunk then + handle:close() + return 1 + else return handle:write(chunk) end + end + else return sink.error(io_err or "unable to open file") end +end + +-- creates a sink that discards data +local function null() + return 1 +end + +function sink.null() + return null +end + +-- creates a sink that just returns an error +function sink.error(err) + return function() + return nil, err + end +end + +-- chains a sink with one or several filter(s) +function sink.chain(f, snk, ...) + if ... then + local args = { f, snk, ... } + snk = table.remove(args, #args) + f = filter.chain(unpack(args)) + end + base.assert(f and snk) + return function(chunk, err) + if chunk ~= "" then + local filtered = f(chunk) + local done = chunk and "" + while true do + local ret, snkerr = snk(filtered, err) + if not ret then return nil, snkerr end + if filtered == done then return 1 end + filtered = f(done) + end + else return 1 end + end +end + +----------------------------------------------------------------------------- +-- Pump stuff +----------------------------------------------------------------------------- +-- pumps one chunk from the source to the sink +function pump.step(src, snk) + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + if chunk and ret then return 1 + else return nil, src_err or snk_err end +end + +-- pumps all data from a source to a sink, using a step function +function pump.all(src, snk, step) + base.assert(src and snk) + step = step or pump.step + while true do + local ret, err = step(src, snk) + if not ret then + if err then return nil, err + else return 1 end + end + end +end + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/mime.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/mime.lua new file mode 100644 index 00000000..b8141ec9 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/mime.lua @@ -0,0 +1,89 @@ +----------------------------------------------------------------------------- +-- MIME support for the Lua language. +-- Author: Diego Nehab +-- Conforming to RFCs 2045-2049 +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local ltn12 = require('lua.libs.luasocket.ltn12') +local mime = require("mime.core") +local string = require("string") +local _M = mime + +-- encode, decode and wrap algorithm tables +local encodet, decodet, wrapt = {},{},{} + +_M.encodet = encodet +_M.decodet = decodet +_M.wrapt = wrapt + +-- creates a function that chooses a filter by name from a given table +local function choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then + base.error("unknown key (" .. base.tostring(name) .. ")", 3) + else return f(opt1, opt2) end + end +end + +-- define the encoding filters +encodet['base64'] = function() + return ltn12.filter.cycle(_M.b64, "") +end + +encodet['quoted-printable'] = function(mode) + return ltn12.filter.cycle(_M.qp, "", + (mode == "binary") and "=0D=0A" or "\r\n") +end + +-- define the decoding filters +decodet['base64'] = function() + return ltn12.filter.cycle(_M.unb64, "") +end + +decodet['quoted-printable'] = function() + return ltn12.filter.cycle(_M.unqp, "") +end + +local function format(chunk) + if chunk then + if chunk == "" then return "''" + else return string.len(chunk) end + else return "nil" end +end + +-- define the line-wrap filters +wrapt['text'] = function(length) + length = length or 76 + return ltn12.filter.cycle(_M.wrp, length, length) +end +wrapt['base64'] = wrapt['text'] +wrapt['default'] = wrapt['text'] + +wrapt['quoted-printable'] = function() + return ltn12.filter.cycle(_M.qpwrp, 76, 76) +end + +-- function that choose the encoding, decoding or wrap algorithm +_M.encode = choose(encodet) +_M.decode = choose(decodet) +_M.wrap = choose(wrapt) + +-- define the end-of-line normalization filter +function _M.normalize(marker) + return ltn12.filter.cycle(_M.eol, 0, marker) +end + +-- high level stuffing filter +function _M.stuff() + return ltn12.filter.cycle(_M.dot, 2) +end + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket.lua new file mode 100644 index 00000000..d1c0b164 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket.lua @@ -0,0 +1,149 @@ +----------------------------------------------------------------------------- +-- LuaSocket helper module +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local math = require("math") +local socket = require("socket.core") + +local _M = socket + +----------------------------------------------------------------------------- +-- Exported auxiliar functions +----------------------------------------------------------------------------- +function _M.connect4(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet") +end + +function _M.connect6(address, port, laddress, lport) + return socket.connect(address, port, laddress, lport, "inet6") +end + +function _M.bind(host, port, backlog) + if host == "*" then host = "0.0.0.0" end + local addrinfo, err = socket.dns.getaddrinfo(host); + if not addrinfo then return nil, err end + local sock, res + err = "no info on address" + for i, alt in base.ipairs(addrinfo) do + if alt.family == "inet" then + sock, err = socket.tcp4() + else + sock, err = socket.tcp6() + end + if not sock then return nil, err end + sock:setoption("reuseaddr", true) + res, err = sock:bind(alt.addr, port) + if not res then + sock:close() + else + res, err = sock:listen(backlog) + if not res then + sock:close() + else + return sock + end + end + end + return nil, err +end + +_M.try = _M.newtry() + +function _M.choose(table) + return function(name, opt1, opt2) + if base.type(name) ~= "string" then + name, opt1, opt2 = "default", name, opt1 + end + local f = table[name or "nil"] + if not f then base.error("unknown key (".. base.tostring(name) ..")", 3) + else return f(opt1, opt2) end + end +end + +----------------------------------------------------------------------------- +-- Socket sources and sinks, conforming to LTN12 +----------------------------------------------------------------------------- +-- create namespaces inside LuaSocket namespace +local sourcet, sinkt = {}, {} +_M.sourcet = sourcet +_M.sinkt = sinkt + +_M.BLOCKSIZE = 2048 + +sinkt["close-when-done"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then + sock:close() + return 1 + else return sock:send(chunk) end + end + }) +end + +sinkt["keep-open"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if chunk then return sock:send(chunk) + else return 1 end + end + }) +end + +sinkt["default"] = sinkt["keep-open"] + +_M.sink = _M.choose(sinkt) + +sourcet["by-length"] = function(sock, length) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if length <= 0 then return nil end + local size = math.min(socket.BLOCKSIZE, length) + local chunk, err = sock:receive(size) + if err then return nil, err end + length = length - string.len(chunk) + return chunk + end + }) +end + +sourcet["until-closed"] = function(sock) + local done + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + if done then return nil end + local chunk, err, partial = sock:receive(socket.BLOCKSIZE) + if not err then return chunk + elseif err == "closed" then + sock:close() + done = 1 + return partial + else return nil, err end + end + }) +end + + +sourcet["default"] = sourcet["until-closed"] + +_M.source = _M.choose(sourcet) + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/ftp.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/ftp.lua new file mode 100644 index 00000000..02b8ed01 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/ftp.lua @@ -0,0 +1,329 @@ +----------------------------------------------------------------------------- +-- FTP support for the Lua language +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local table = require("table") +local string = require("string") +local math = require("math") +local socket = require('lua.libs.luasocket.socket') +local url = require('lua.libs.luasocket.socket.url') +local tp = require('lua.libs.luasocket.socket.tp') +local ltn12 = require('lua.libs.luasocket.ltn12') +socket.ftp = {} +local _M = socket.ftp +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout in seconds before the program gives up on a connection +_M.TIMEOUT = 60 +-- default port for ftp service +local PORT = 21 +-- this is the default anonymous password. used when no password is +-- provided in url. should be changed to your e-mail. +_M.USER = "ftp" +_M.PASSWORD = "anonymous@anonymous.org" + +----------------------------------------------------------------------------- +-- Low level FTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server, port or PORT, _M.TIMEOUT, create)) + local f = base.setmetatable({ tp = tp }, metat) + -- make sure everything gets closed in an exception + f.try = socket.newtry(function() f:close() end) + return f +end + +function metat.__index:portconnect() + self.try(self.server:settimeout(_M.TIMEOUT)) + self.data = self.try(self.server:accept()) + self.try(self.data:settimeout(_M.TIMEOUT)) +end + +function metat.__index:pasvconnect() + self.data = self.try(socket.tcp()) + self.try(self.data:settimeout(_M.TIMEOUT)) + self.try(self.data:connect(self.pasvt.address, self.pasvt.port)) +end + +function metat.__index:login(user, password) + self.try(self.tp:command("user", user or _M.USER)) + local code, reply = self.try(self.tp:check{"2..", 331}) + if code == 331 then + self.try(self.tp:command("pass", password or _M.PASSWORD)) + self.try(self.tp:check("2..")) + end + return 1 +end + +function metat.__index:pasv() + self.try(self.tp:command("pasv")) + local code, reply = self.try(self.tp:check("2..")) + local pattern = "(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)%D(%d+)" + local a, b, c, d, p1, p2 = socket.skip(2, string.find(reply, pattern)) + self.try(a and b and c and d and p1 and p2, reply) + self.pasvt = { + address = string.format("%d.%d.%d.%d", a, b, c, d), + port = p1*256 + p2 + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + +function metat.__index:epsv() + self.try(self.tp:command("epsv")) + local code, reply = self.try(self.tp:check("229")) + local pattern = "%((.)(.-)%1(.-)%1(.-)%1%)" + local d, prt, address, port = string.match(reply, pattern) + self.try(port, "invalid epsv response") + self.pasvt = { + address = self.tp:getpeername(), + port = port + } + if self.server then + self.server:close() + self.server = nil + end + return self.pasvt.address, self.pasvt.port +end + + +function metat.__index:port(address, port) + self.pasvt = nil + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local pl = math.mod(port, 256) + local ph = (port - pl)/256 + local arg = string.gsub(string.format("%s,%d,%d", address, ph, pl), "%.", ",") + self.try(self.tp:command("port", arg)) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:eprt(family, address, port) + self.pasvt = nil + if not address then + address, port = self.try(self.tp:getsockname()) + self.server = self.try(socket.bind(address, 0)) + address, port = self.try(self.server:getsockname()) + self.try(self.server:settimeout(_M.TIMEOUT)) + end + local arg = string.format("|%s|%s|%d|", family, address, port) + self.try(self.tp:command("eprt", arg)) + self.try(self.tp:check("2..")) + return 1 +end + + +function metat.__index:send(sendt) + self.try(self.pasvt or self.server, "need port or pasv first") + -- if there is a pasvt table, we already sent a PASV command + -- we just get the data connection into self.data + if self.pasvt then self:pasvconnect() end + -- get the transfer argument and command + local argument = sendt.argument or + url.unescape(string.gsub(sendt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = sendt.command or "stor" + -- send the transfer command and check the reply + self.try(self.tp:command(command, argument)) + local code, reply = self.try(self.tp:check{"2..", "1.."}) + -- if there is not a pasvt table, then there is a server + -- and we already sent a PORT command + if not self.pasvt then self:portconnect() end + -- get the sink, source and step for the transfer + local step = sendt.step or ltn12.pump.step + local readt = { self.tp } + local checkstep = function(src, snk) + -- check status in control connection while downloading + local readyt = socket.select(readt, nil, 0) + if readyt[tp] then code = self.try(self.tp:check("2..")) end + return step(src, snk) + end + local sink = socket.sink("close-when-done", self.data) + -- transfer all data and check error + self.try(ltn12.pump.all(sendt.source, sink, checkstep)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + -- done with data connection + self.data:close() + -- find out how many bytes were sent + local sent = socket.skip(1, self.data:getstats()) + self.data = nil + return sent +end + +function metat.__index:receive(recvt) + self.try(self.pasvt or self.server, "need port or pasv first") + if self.pasvt then self:pasvconnect() end + local argument = recvt.argument or + url.unescape(string.gsub(recvt.path or "", "^[/\\]", "")) + if argument == "" then argument = nil end + local command = recvt.command or "retr" + self.try(self.tp:command(command, argument)) + local code,reply = self.try(self.tp:check{"1..", "2.."}) + if (code >= 200) and (code <= 299) then + recvt.sink(reply) + return 1 + end + if not self.pasvt then self:portconnect() end + local source = socket.source("until-closed", self.data) + local step = recvt.step or ltn12.pump.step + self.try(ltn12.pump.all(source, recvt.sink, step)) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + self.data:close() + self.data = nil + return 1 +end + +function metat.__index:cwd(dir) + self.try(self.tp:command("cwd", dir)) + self.try(self.tp:check(250)) + return 1 +end + +function metat.__index:type(type) + self.try(self.tp:command("type", type)) + self.try(self.tp:check(200)) + return 1 +end + +function metat.__index:greet() + local code = self.try(self.tp:check{"1..", "2.."}) + if string.find(code, "1..") then self.try(self.tp:check("2..")) end + return 1 +end + +function metat.__index:quit() + self.try(self.tp:command("quit")) + self.try(self.tp:check("2..")) + return 1 +end + +function metat.__index:close() + if self.data then self.data:close() end + if self.server then self.server:close() end + return self.tp:close() +end + +----------------------------------------------------------------------------- +-- High level FTP API +----------------------------------------------------------------------------- +local function override(t) + if t.url then + local u = url.parse(t.url) + for i,v in base.pairs(t) do + u[i] = v + end + return u + else return t end +end + +local function tput(putt) + putt = override(putt) + socket.try(putt.host, "missing hostname") + local f = _M.open(putt.host, putt.port, putt.create) + f:greet() + f:login(putt.user, putt.password) + if putt.type then f:type(putt.type) end + f:epsv() + local sent = f:send(putt) + f:quit() + f:close() + return sent +end + +local default = { + path = "/", + scheme = "ftp" +} + +local function genericform(u) + local t = socket.try(url.parse(u, default)) + socket.try(t.scheme == "ftp", "wrong scheme '" .. t.scheme .. "'") + socket.try(t.host, "missing hostname") + local pat = "^type=(.)$" + if t.params then + t.type = socket.skip(2, string.find(t.params, pat)) + socket.try(t.type == "a" or t.type == "i", + "invalid type '" .. t.type .. "'") + end + return t +end + +_M.genericform = genericform + +local function sput(u, body) + local putt = genericform(u) + putt.source = ltn12.source.string(body) + return tput(putt) +end + +_M.put = socket.protect(function(putt, body) + if base.type(putt) == "string" then return sput(putt, body) + else return tput(putt) end +end) + +local function tget(gett) + gett = override(gett) + socket.try(gett.host, "missing hostname") + local f = _M.open(gett.host, gett.port, gett.create) + f:greet() + f:login(gett.user, gett.password) + if gett.type then f:type(gett.type) end + f:epsv() + f:receive(gett) + f:quit() + return f:close() +end + +local function sget(u) + local gett = genericform(u) + local t = {} + gett.sink = ltn12.sink.table(t) + tget(gett) + return table.concat(t) +end + +_M.command = socket.protect(function(cmdt) + cmdt = override(cmdt) + socket.try(cmdt.host, "missing hostname") + socket.try(cmdt.command, "missing command") + local f = _M.open(cmdt.host, cmdt.port, cmdt.create) + f:greet() + f:login(cmdt.user, cmdt.password) + if type(cmdt.command) == "table" then + local argument = cmdt.argument or {} + local check = cmdt.check or {} + for i,cmd in ipairs(cmdt.command) do + f.try(f.tp:command(cmd, argument[i])) + if check[i] then f.try(f.tp:check(check[i])) end + end + else + f.try(f.tp:command(cmdt.command, cmdt.argument)) + if cmdt.check then f.try(f.tp:check(cmdt.check)) end + end + f:quit() + return f:close() +end) + +_M.get = socket.protect(function(gett) + if base.type(gett) == "string" then return sget(gett) + else return tget(gett) end +end) + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/headers.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/headers.lua new file mode 100644 index 00000000..a7ce3c78 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/headers.lua @@ -0,0 +1,104 @@ +----------------------------------------------------------------------------- +-- Canonic header field capitalization +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- +local socket = require('lua.libs.luasocket.socket') +socket.headers = {} +local _M = socket.headers + +_M.canonic = { + ["accept"] = "Accept", + ["accept-charset"] = "Accept-Charset", + ["accept-encoding"] = "Accept-Encoding", + ["accept-language"] = "Accept-Language", + ["accept-ranges"] = "Accept-Ranges", + ["action"] = "Action", + ["alternate-recipient"] = "Alternate-Recipient", + ["age"] = "Age", + ["allow"] = "Allow", + ["arrival-date"] = "Arrival-Date", + ["authorization"] = "Authorization", + ["bcc"] = "Bcc", + ["cache-control"] = "Cache-Control", + ["cc"] = "Cc", + ["comments"] = "Comments", + ["connection"] = "Connection", + ["content-description"] = "Content-Description", + ["content-disposition"] = "Content-Disposition", + ["content-encoding"] = "Content-Encoding", + ["content-id"] = "Content-ID", + ["content-language"] = "Content-Language", + ["content-length"] = "Content-Length", + ["content-location"] = "Content-Location", + ["content-md5"] = "Content-MD5", + ["content-range"] = "Content-Range", + ["content-transfer-encoding"] = "Content-Transfer-Encoding", + ["content-type"] = "Content-Type", + ["cookie"] = "Cookie", + ["date"] = "Date", + ["diagnostic-code"] = "Diagnostic-Code", + ["dsn-gateway"] = "DSN-Gateway", + ["etag"] = "ETag", + ["expect"] = "Expect", + ["expires"] = "Expires", + ["final-log-id"] = "Final-Log-ID", + ["final-recipient"] = "Final-Recipient", + ["from"] = "From", + ["host"] = "Host", + ["if-match"] = "If-Match", + ["if-modified-since"] = "If-Modified-Since", + ["if-none-match"] = "If-None-Match", + ["if-range"] = "If-Range", + ["if-unmodified-since"] = "If-Unmodified-Since", + ["in-reply-to"] = "In-Reply-To", + ["keywords"] = "Keywords", + ["last-attempt-date"] = "Last-Attempt-Date", + ["last-modified"] = "Last-Modified", + ["location"] = "Location", + ["max-forwards"] = "Max-Forwards", + ["message-id"] = "Message-ID", + ["mime-version"] = "MIME-Version", + ["original-envelope-id"] = "Original-Envelope-ID", + ["original-recipient"] = "Original-Recipient", + ["pragma"] = "Pragma", + ["proxy-authenticate"] = "Proxy-Authenticate", + ["proxy-authorization"] = "Proxy-Authorization", + ["range"] = "Range", + ["received"] = "Received", + ["received-from-mta"] = "Received-From-MTA", + ["references"] = "References", + ["referer"] = "Referer", + ["remote-mta"] = "Remote-MTA", + ["reply-to"] = "Reply-To", + ["reporting-mta"] = "Reporting-MTA", + ["resent-bcc"] = "Resent-Bcc", + ["resent-cc"] = "Resent-Cc", + ["resent-date"] = "Resent-Date", + ["resent-from"] = "Resent-From", + ["resent-message-id"] = "Resent-Message-ID", + ["resent-reply-to"] = "Resent-Reply-To", + ["resent-sender"] = "Resent-Sender", + ["resent-to"] = "Resent-To", + ["retry-after"] = "Retry-After", + ["return-path"] = "Return-Path", + ["sender"] = "Sender", + ["server"] = "Server", + ["smtp-remote-recipient"] = "SMTP-Remote-Recipient", + ["status"] = "Status", + ["subject"] = "Subject", + ["te"] = "TE", + ["to"] = "To", + ["trailer"] = "Trailer", + ["transfer-encoding"] = "Transfer-Encoding", + ["upgrade"] = "Upgrade", + ["user-agent"] = "User-Agent", + ["vary"] = "Vary", + ["via"] = "Via", + ["warning"] = "Warning", + ["will-retry-until"] = "Will-Retry-Until", + ["www-authenticate"] = "WWW-Authenticate", + ["x-mailer"] = "X-Mailer", +} + +return _M \ No newline at end of file diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/http.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/http.lua new file mode 100644 index 00000000..9c051aba --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/http.lua @@ -0,0 +1,420 @@ +----------------------------------------------------------------------------- +-- HTTP/1.1 client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +------------------------------------------------------------------------------- +local socket = require('lua.libs.luasocket.socket') +local url = require('lua.libs.luasocket.socket.url') +local ltn12 = require('lua.libs.luasocket.ltn12') +local mime = require('lua.libs.luasocket.mime') +local string = require("string") +local headers = require('lua.libs.luasocket.socket.headers') +local base = _G +local table = require("table") +socket.http = {} +local _M = socket.http + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- connection timeout in seconds +_M.TIMEOUT = 60 +-- user agent field sent in request +_M.USERAGENT = socket._VERSION + +-- supported schemes and their particulars +local SCHEMES = { + http = { + port = 80 + , create = function(t) + return socket.tcp end } + , https = { + port = 443 + , create = function(t) + local https = assert( + require("ssl.https"), 'LuaSocket: LuaSec not found') + local tcp = assert( + https.tcp, 'LuaSocket: Function tcp() not available from LuaSec') + return tcp(t) end }} + +-- default scheme and port for document retrieval +local SCHEME = 'http' +local PORT = SCHEMES[SCHEME].port +----------------------------------------------------------------------------- +-- Reads MIME headers from a connection, unfolding where needed +----------------------------------------------------------------------------- +local function receiveheaders(sock, headers) + local line, name, value, err + headers = headers or {} + -- get first line + line, err = sock:receive() + if err then return nil, err end + -- headers go until a blank line is found + while line ~= "" do + -- get field-name and value + name, value = socket.skip(2, string.find(line, "^(.-):%s*(.*)")) + if not (name and value) then return nil, "malformed reponse headers" end + name = string.lower(name) + -- get next line (value might be folded) + line, err = sock:receive() + if err then return nil, err end + -- unfold any folded values + while string.find(line, "^%s") do + value = value .. line + line = sock:receive() + if err then return nil, err end + end + -- save pair in table + if headers[name] then headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + return headers +end + +----------------------------------------------------------------------------- +-- Extra sources and sinks +----------------------------------------------------------------------------- +socket.sourcet["http-chunked"] = function(sock, headers) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function() + -- get chunk size, skip extention + local line, err = sock:receive() + if err then return nil, err end + local size = base.tonumber(string.gsub(line, ";.*", ""), 16) + if not size then return nil, "invalid chunk size" end + -- was it the last chunk? + if size > 0 then + -- if not, get chunk and skip terminating CRLF + local chunk, err, part = sock:receive(size) + if chunk then sock:receive() end + return chunk, err + else + -- if it was, read trailers into headers table + headers, err = receiveheaders(sock, headers) + if not headers then return nil, err end + end + end + }) +end + +socket.sinkt["http-chunked"] = function(sock) + return base.setmetatable({ + getfd = function() return sock:getfd() end, + dirty = function() return sock:dirty() end + }, { + __call = function(self, chunk, err) + if not chunk then return sock:send("0\r\n\r\n") end + local size = string.format("%X\r\n", string.len(chunk)) + return sock:send(size .. chunk .. "\r\n") + end + }) +end + +----------------------------------------------------------------------------- +-- Low level HTTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function _M.open(host, port, create) + -- create socket with user connect function, or with default + local c = socket.try(create()) + local h = base.setmetatable({ c = c }, metat) + -- create finalized try + h.try = socket.newtry(function() h:close() end) + -- set timeout before connecting + h.try(c:settimeout(_M.TIMEOUT)) + h.try(c:connect(host, port)) + -- here everything worked + return h +end + +function metat.__index:sendrequestline(method, uri) + local reqline = string.format("%s %s HTTP/1.1\r\n", method or "GET", uri) + return self.try(self.c:send(reqline)) +end + +function metat.__index:sendheaders(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f, v in base.pairs(tosend) do + h = (canonic[f] or f) .. ": " .. v .. "\r\n" .. h + end + self.try(self.c:send(h)) + return 1 +end + +function metat.__index:sendbody(headers, source, step) + source = source or ltn12.source.empty() + step = step or ltn12.pump.step + -- if we don't know the size in advance, send chunked and hope for the best + local mode = "http-chunked" + if headers["content-length"] then mode = "keep-open" end + return self.try(ltn12.pump.all(source, socket.sink(mode, self.c), step)) +end + +function metat.__index:receivestatusline() + local status,ec = self.try(self.c:receive(5)) + -- identify HTTP/0.9 responses, which do not contain a status line + -- this is just a heuristic, but is what the RFC recommends + if status ~= "HTTP/" then + if ec == "timeout" then + return 408 + end + return nil, status + end + -- otherwise proceed reading a status line + status = self.try(self.c:receive("*l", status)) + local code = socket.skip(2, string.find(status, "HTTP/%d*%.%d* (%d%d%d)")) + return self.try(base.tonumber(code), status) +end + +function metat.__index:receiveheaders() + return self.try(receiveheaders(self.c)) +end + +function metat.__index:receivebody(headers, sink, step) + sink = sink or ltn12.sink.null() + step = step or ltn12.pump.step + local length = base.tonumber(headers["content-length"]) + local t = headers["transfer-encoding"] -- shortcut + local mode = "default" -- connection close + if t and t ~= "identity" then mode = "http-chunked" + elseif base.tonumber(headers["content-length"]) then mode = "by-length" end + return self.try(ltn12.pump.all(socket.source(mode, self.c, length), + sink, step)) +end + +function metat.__index:receive09body(status, sink, step) + local source = ltn12.source.rewind(socket.source("until-closed", self.c)) + source(status) + return self.try(ltn12.pump.all(source, sink, step)) +end + +function metat.__index:close() + return self.c:close() +end + +----------------------------------------------------------------------------- +-- High level HTTP API +----------------------------------------------------------------------------- +local function adjusturi(reqt) + local u = reqt + -- if there is a proxy, we need the full url. otherwise, just a part. + if not reqt.proxy and not _M.PROXY then + u = { + path = socket.try(reqt.path, "invalid path 'nil'"), + params = reqt.params, + query = reqt.query, + fragment = reqt.fragment + } + end + return url.build(u) +end + +local function adjustproxy(reqt) + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + return proxy.host, proxy.port or 3128 + else + return reqt.host, reqt.port + end +end + +local function adjustheaders(reqt) + -- default headers + local host = reqt.host + local port = tostring(reqt.port) + if port ~= tostring(SCHEMES[reqt.scheme].port) then + host = host .. ':' .. port end + local lower = { + ["user-agent"] = _M.USERAGENT, + ["host"] = host, + ["connection"] = "close, TE", + ["te"] = "trailers" + } + -- if we have authentication information, pass it along + if reqt.user and reqt.password then + lower["authorization"] = + "Basic " .. (mime.b64(reqt.user .. ":" .. + url.unescape(reqt.password))) + end + -- if we have proxy authentication information, pass it along + local proxy = reqt.proxy or _M.PROXY + if proxy then + proxy = url.parse(proxy) + if proxy.user and proxy.password then + lower["proxy-authorization"] = + "Basic " .. (mime.b64(proxy.user .. ":" .. proxy.password)) + end + end + -- override with user headers + for i,v in base.pairs(reqt.headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +-- default url parts +local default = { + path ="/" + , scheme = "http" +} + +local function adjustrequest(reqt) + -- parse url if provided + local nreqt = reqt.url and url.parse(reqt.url, default) or {} + -- explicit components override url + for i,v in base.pairs(reqt) do nreqt[i] = v end + -- default to scheme particulars + local schemedefs, host, port, method + = SCHEMES[nreqt.scheme], nreqt.host, nreqt.port, nreqt.method + if not nreqt.create then nreqt.create = schemedefs.create(nreqt) end + if not (port and port ~= '') then nreqt.port = schemedefs.port end + if not (method and method ~= '') then nreqt.method = 'GET' end + if not (host and host ~= "") then + socket.try(nil, "invalid host '" .. base.tostring(nreqt.host) .. "'") + end + -- compute uri if user hasn't overriden + nreqt.uri = reqt.uri or adjusturi(nreqt) + -- adjust headers in request + nreqt.headers = adjustheaders(nreqt) + -- ajust host and port if there is a proxy + nreqt.host, nreqt.port = adjustproxy(nreqt) + return nreqt +end + +local function shouldredirect(reqt, code, headers) + local location = headers.location + if not location then return false end + location = string.gsub(location, "%s", "") + if location == "" then return false end + local scheme = url.parse(location).scheme + if scheme and (not SCHEMES[scheme]) then return false end + -- avoid https downgrades + if ('https' == reqt.scheme) and ('https' ~= scheme) then return false end + return (reqt.redirect ~= false) and + (code == 301 or code == 302 or code == 303 or code == 307) and + (not reqt.method or reqt.method == "GET" or reqt.method == "HEAD") + and ((false == reqt.maxredirects) + or ((reqt.nredirects or 0) + < (reqt.maxredirects or 5))) +end + +local function shouldreceivebody(reqt, code) + if reqt.method == "HEAD" then return nil end + if code == 204 or code == 304 then return nil end + if code >= 100 and code < 200 then return nil end + return 1 +end + +-- forward declarations +local trequest, tredirect + +--[[local]] function tredirect(reqt, location) + -- the RFC says the redirect URL has to be absolute, but some + -- servers do not respect that + local newurl = url.absolute(reqt.url, location) + -- if switching schemes, reset port and create function + if url.parse(newurl).scheme ~= reqt.scheme then + reqt.port = nil + reqt.create = nil end + -- make new request + local result, code, headers, status = trequest { + url = newurl, + source = reqt.source, + sink = reqt.sink, + headers = reqt.headers, + proxy = reqt.proxy, + maxredirects = reqt.maxredirects, + nredirects = (reqt.nredirects or 0) + 1, + create = reqt.create + } + -- pass location header back as a hint we redirected + headers = headers or {} + headers.location = headers.location or location + return result, code, headers, status +end + +--[[local]] function trequest(reqt) + -- we loop until we get what we want, or + -- until we are sure there is no way to get it + local nreqt = adjustrequest(reqt) + local h = _M.open(nreqt.host, nreqt.port, nreqt.create) + -- send request line and headers + h:sendrequestline(nreqt.method, nreqt.uri) + h:sendheaders(nreqt.headers) + -- if there is a body, send it + if nreqt.source then + h:sendbody(nreqt.headers, nreqt.source, nreqt.step) + end + local code, status = h:receivestatusline() + -- if it is an HTTP/0.9 server, simply get the body and we are done + if not code then + h:receive09body(status, nreqt.sink, nreqt.step) + return 1, 200 + elseif code == 408 then + return 1, code + end + local headers + -- ignore any 100-continue messages + while code == 100 do + headers = h:receiveheaders() + code, status = h:receivestatusline() + end + headers = h:receiveheaders() + -- at this point we should have a honest reply from the server + -- we can't redirect if we already used the source, so we report the error + if shouldredirect(nreqt, code, headers) and not nreqt.source then + h:close() + return tredirect(reqt, headers.location) + end + -- here we are finally done + if shouldreceivebody(nreqt, code) then + h:receivebody(headers, nreqt.sink, nreqt.step) + end + h:close() + return 1, code, headers, status +end + +-- turns an url and a body into a generic request +local function genericform(u, b) + local t = {} + local reqt = { + url = u, + sink = ltn12.sink.table(t), + target = t + } + if b then + reqt.source = ltn12.source.string(b) + reqt.headers = { + ["content-length"] = string.len(b), + ["content-type"] = "application/x-www-form-urlencoded" + } + reqt.method = "POST" + end + return reqt +end + +_M.genericform = genericform + +local function srequest(u, b) + local reqt = genericform(u, b) + local _, code, headers, status = trequest(reqt) + return table.concat(reqt.target), code, headers, status +end + +_M.request = socket.protect(function(reqt, body) + if base.type(reqt) == "string" then return srequest(reqt, body) + else return trequest(reqt) end +end) + +_M.schemes = SCHEMES +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/mbox.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/mbox.lua new file mode 100644 index 00000000..ed9e7814 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/mbox.lua @@ -0,0 +1,92 @@ +local _M = {} + +if module then + mbox = _M +end + +function _M.split_message(message_s) + local message = {} + message_s = string.gsub(message_s, "\r\n", "\n") + string.gsub(message_s, "^(.-\n)\n", function (h) message.headers = h end) + string.gsub(message_s, "^.-\n\n(.*)", function (b) message.body = b end) + if not message.body then + string.gsub(message_s, "^\n(.*)", function (b) message.body = b end) + end + if not message.headers and not message.body then + message.headers = message_s + end + return message.headers or "", message.body or "" +end + +function _M.split_headers(headers_s) + local headers = {} + headers_s = string.gsub(headers_s, "\r\n", "\n") + headers_s = string.gsub(headers_s, "\n[ ]+", " ") + string.gsub("\n" .. headers_s, "\n([^\n]+)", function (h) table.insert(headers, h) end) + return headers +end + +function _M.parse_header(header_s) + header_s = string.gsub(header_s, "\n[ ]+", " ") + header_s = string.gsub(header_s, "\n+", "") + local _, __, name, value = string.find(header_s, "([^%s:]-):%s*(.*)") + return name, value +end + +function _M.parse_headers(headers_s) + local headers_t = _M.split_headers(headers_s) + local headers = {} + for i = 1, #headers_t do + local name, value = _M.parse_header(headers_t[i]) + if name then + name = string.lower(name) + if headers[name] then + headers[name] = headers[name] .. ", " .. value + else headers[name] = value end + end + end + return headers +end + +function _M.parse_from(from) + local _, __, name, address = string.find(from, "^%s*(.-)%s*%<(.-)%>") + if not address then + _, __, address = string.find(from, "%s*(.+)%s*") + end + name = name or "" + address = address or "" + if name == "" then name = address end + name = string.gsub(name, '"', "") + return name, address +end + +function _M.split_mbox(mbox_s) + local mbox = {} + mbox_s = string.gsub(mbox_s, "\r\n", "\n") .."\n\nFrom \n" + local nj, i, j = 1, 1, 1 + while 1 do + i, nj = string.find(mbox_s, "\n\nFrom .-\n", j) + if not i then break end + local message = string.sub(mbox_s, j, i-1) + table.insert(mbox, message) + j = nj+1 + end + return mbox +end + +function _M.parse(mbox_s) + local mbox = _M.split_mbox(mbox_s) + for i = 1, #mbox do + mbox[i] = _M.parse_message(mbox[i]) + end + return mbox +end + +function _M.parse_message(message_s) + local message = {} + message.headers, message.body = _M.split_message(message_s) + message.headers = _M.parse_headers(message.headers) + return message +end + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/smtp.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/smtp.lua new file mode 100644 index 00000000..28db4ff6 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/smtp.lua @@ -0,0 +1,256 @@ +----------------------------------------------------------------------------- +-- SMTP client support for the Lua language. +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local coroutine = require("coroutine") +local string = require("string") +local math = require("math") +local os = require("os") +local socket = require('lua.libs.luasocket.socket') +local tp = require('lua.libs.luasocket.socket.tp') +local ltn12 = require('lua.libs.luasocket.ltn12') +local headers = require('lua.libs.luasocket.socket.headers') +local mime = require('lua.libs.luasocket.mime') + +socket.smtp = {} +local _M = socket.smtp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +-- timeout for connection +_M.TIMEOUT = 60 +-- default server used to send e-mails +_M.SERVER = "localhost" +-- default port +_M.PORT = 25 +-- domain used in HELO command and default sendmail +-- If we are under a CGI, try to get from environment +_M.DOMAIN = os.getenv("SERVER_NAME") or "localhost" +-- default time zone (means we don't know) +_M.ZONE = "-0000" + +--------------------------------------------------------------------------- +-- Low level SMTP API +----------------------------------------------------------------------------- +local metat = { __index = {} } + +function metat.__index:greet(domain) + self.try(self.tp:check("2..")) + self.try(self.tp:command("EHLO", domain or _M.DOMAIN)) + return socket.skip(1, self.try(self.tp:check("2.."))) +end + +function metat.__index:mail(from) + self.try(self.tp:command("MAIL", "FROM:" .. from)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:rcpt(to) + self.try(self.tp:command("RCPT", "TO:" .. to)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:data(src, step) + self.try(self.tp:command("DATA")) + self.try(self.tp:check("3..")) + self.try(self.tp:source(src, step)) + self.try(self.tp:send("\r\n.\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:quit() + self.try(self.tp:command("QUIT")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:close() + return self.tp:close() +end + +function metat.__index:login(user, password) + self.try(self.tp:command("AUTH", "LOGIN")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(user) .. "\r\n")) + self.try(self.tp:check("3..")) + self.try(self.tp:send(mime.b64(password) .. "\r\n")) + return self.try(self.tp:check("2..")) +end + +function metat.__index:plain(user, password) + local auth = "PLAIN " .. mime.b64("\0" .. user .. "\0" .. password) + self.try(self.tp:command("AUTH", auth)) + return self.try(self.tp:check("2..")) +end + +function metat.__index:auth(user, password, ext) + if not user or not password then return 1 end + if string.find(ext, "AUTH[^\n]+LOGIN") then + return self:login(user, password) + elseif string.find(ext, "AUTH[^\n]+PLAIN") then + return self:plain(user, password) + else + self.try(nil, "authentication not supported") + end +end + +-- send message or throw an exception +function metat.__index:send(mailt) + self:mail(mailt.from) + if base.type(mailt.rcpt) == "table" then + for i,v in base.ipairs(mailt.rcpt) do + self:rcpt(v) + end + else + self:rcpt(mailt.rcpt) + end + self:data(ltn12.source.chain(mailt.source, mime.stuff()), mailt.step) +end + +function _M.open(server, port, create) + local tp = socket.try(tp.connect(server or _M.SERVER, port or _M.PORT, + _M.TIMEOUT, create)) + local s = base.setmetatable({tp = tp}, metat) + -- make sure tp is closed if we get an exception + s.try = socket.newtry(function() + s:close() + end) + return s +end + +-- convert headers to lowercase +local function lower_headers(headers) + local lower = {} + for i,v in base.pairs(headers or lower) do + lower[string.lower(i)] = v + end + return lower +end + +--------------------------------------------------------------------------- +-- Multipart message source +----------------------------------------------------------------------------- +-- returns a hopefully unique mime boundary +local seqno = 0 +local function newboundary() + seqno = seqno + 1 + return string.format('%s%05d==%05u', os.date('%d%m%Y%H%M%S'), + math.random(0, 99999), seqno) +end + +-- send_message forward declaration +local send_message + +-- yield the headers all at once, it's faster +local function send_headers(tosend) + local canonic = headers.canonic + local h = "\r\n" + for f,v in base.pairs(tosend) do + h = (canonic[f] or f) .. ': ' .. v .. "\r\n" .. h + end + coroutine.yield(h) +end + +-- yield multipart message body from a multipart message table +local function send_multipart(mesgt) + -- make sure we have our boundary and send headers + local bd = newboundary() + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or 'multipart/mixed' + headers['content-type'] = headers['content-type'] .. + '; boundary="' .. bd .. '"' + send_headers(headers) + -- send preamble + if mesgt.body.preamble then + coroutine.yield(mesgt.body.preamble) + coroutine.yield("\r\n") + end + -- send each part separated by a boundary + for i, m in base.ipairs(mesgt.body) do + coroutine.yield("\r\n--" .. bd .. "\r\n") + send_message(m) + end + -- send last boundary + coroutine.yield("\r\n--" .. bd .. "--\r\n\r\n") + -- send epilogue + if mesgt.body.epilogue then + coroutine.yield(mesgt.body.epilogue) + coroutine.yield("\r\n") + end +end + +-- yield message body from a source +local function send_source(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from source + while true do + local chunk, err = mesgt.body() + if err then coroutine.yield(nil, err) + elseif chunk then coroutine.yield(chunk) + else break end + end +end + +-- yield message body from a string +local function send_string(mesgt) + -- make sure we have a content-type + local headers = lower_headers(mesgt.headers or {}) + headers['content-type'] = headers['content-type'] or + 'text/plain; charset="iso-8859-1"' + send_headers(headers) + -- send body from string + coroutine.yield(mesgt.body) +end + +-- message source +function send_message(mesgt) + if base.type(mesgt.body) == "table" then send_multipart(mesgt) + elseif base.type(mesgt.body) == "function" then send_source(mesgt) + else send_string(mesgt) end +end + +-- set defaul headers +local function adjust_headers(mesgt) + local lower = lower_headers(mesgt.headers) + lower["date"] = lower["date"] or + os.date("!%a, %d %b %Y %H:%M:%S ") .. (mesgt.zone or _M.ZONE) + lower["x-mailer"] = lower["x-mailer"] or socket._VERSION + -- this can't be overriden + lower["mime-version"] = "1.0" + return lower +end + +function _M.message(mesgt) + mesgt.headers = adjust_headers(mesgt) + -- create and return message source + local co = coroutine.create(function() send_message(mesgt) end) + return function() + local ret, a, b = coroutine.resume(co) + if ret then return a, b + else return nil, a end + end +end + +--------------------------------------------------------------------------- +-- High level SMTP API +----------------------------------------------------------------------------- +_M.send = socket.protect(function(mailt) + local s = _M.open(mailt.server, mailt.port, mailt.create) + local ext = s:greet(mailt.domain) + s:auth(mailt.user, mailt.password, ext) + s:send(mailt) + s:quit() + return s:close() +end) + +return _M \ No newline at end of file diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/tp.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/tp.lua new file mode 100644 index 00000000..19b8d067 --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/tp.lua @@ -0,0 +1,134 @@ +----------------------------------------------------------------------------- +-- Unified SMTP/FTP subsystem +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module and import dependencies +----------------------------------------------------------------------------- +local base = _G +local string = require("string") +local socket = require('lua.libs.luasocket.socket') +local ltn12 = require('lua.libs.luasocket.ltn12') + +socket.tp = {} +local _M = socket.tp + +----------------------------------------------------------------------------- +-- Program constants +----------------------------------------------------------------------------- +_M.TIMEOUT = 60 + +----------------------------------------------------------------------------- +-- Implementation +----------------------------------------------------------------------------- +-- gets server reply (works for SMTP and FTP) +local function get_reply(c) + local code, current, sep + local line, err = c:receive() + local reply = line + if err then return nil, err end + code, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + if not code then return nil, "invalid server reply" end + if sep == "-" then -- reply is multiline + repeat + line, err = c:receive() + if err then return nil, err end + current, sep = socket.skip(2, string.find(line, "^(%d%d%d)(.?)")) + reply = reply .. "\n" .. line + -- reply ends with same code + until code == current and sep == " " + end + return code, reply +end + +-- metatable for sock object +local metat = { __index = {} } + +function metat.__index:getpeername() + return self.c:getpeername() +end + +function metat.__index:getsockname() + return self.c:getpeername() +end + +function metat.__index:check(ok) + local code, reply = get_reply(self.c) + if not code then return nil, reply end + if base.type(ok) ~= "function" then + if base.type(ok) == "table" then + for i, v in base.ipairs(ok) do + if string.find(code, v) then + return base.tonumber(code), reply + end + end + return nil, reply + else + if string.find(code, ok) then return base.tonumber(code), reply + else return nil, reply end + end + else return ok(base.tonumber(code), reply) end +end + +function metat.__index:command(cmd, arg) + cmd = string.upper(cmd) + if arg then + return self.c:send(cmd .. " " .. arg.. "\r\n") + else + return self.c:send(cmd .. "\r\n") + end +end + +function metat.__index:sink(snk, pat) + local chunk, err = self.c:receive(pat) + return snk(chunk, err) +end + +function metat.__index:send(data) + return self.c:send(data) +end + +function metat.__index:receive(pat) + return self.c:receive(pat) +end + +function metat.__index:getfd() + return self.c:getfd() +end + +function metat.__index:dirty() + return self.c:dirty() +end + +function metat.__index:getcontrol() + return self.c +end + +function metat.__index:source(source, step) + local sink = socket.sink("keep-open", self.c) + local ret, err = ltn12.pump.all(source, sink, step or ltn12.pump.step) + return ret, err +end + +-- closes the underlying c +function metat.__index:close() + self.c:close() + return 1 +end + +-- connect with server and return c object +function _M.connect(host, port, timeout, create) + local c, e = (create or socket.tcp)() + if not c then return nil, e end + c:settimeout(timeout or _M.TIMEOUT) + local r, e = c:connect(host, port) + if not r then + c:close() + return nil, e + end + return base.setmetatable({c = c}, metat) +end + +return _M diff --git a/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/url.lua b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/url.lua new file mode 100644 index 00000000..b664307f --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/libs/luasocket/socket/url.lua @@ -0,0 +1,331 @@ +----------------------------------------------------------------------------- +-- URI parsing, composition and relative URL resolution +-- LuaSocket toolkit. +-- Author: Diego Nehab +----------------------------------------------------------------------------- + +----------------------------------------------------------------------------- +-- Declare module +----------------------------------------------------------------------------- +local string = require("string") +local base = _G +local table = require("table") +local socket = require('lua.libs.luasocket.socket') + +socket.url = {} +local _M = socket.url + +----------------------------------------------------------------------------- +-- Module version +----------------------------------------------------------------------------- +_M._VERSION = "URL 1.0.3" + +----------------------------------------------------------------------------- +-- Encodes a string into its escaped hexadecimal representation +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +function _M.escape(s) + return (string.gsub(s, "([^A-Za-z0-9_])", function(c) + return string.format("%%%02x", string.byte(c)) + end)) +end + +----------------------------------------------------------------------------- +-- Protects a path segment, to prevent it from interfering with the +-- url parsing. +-- Input +-- s: binary string to be encoded +-- Returns +-- escaped representation of string binary +----------------------------------------------------------------------------- +local function make_set(t) + local s = {} + for i,v in base.ipairs(t) do + s[t[i]] = 1 + end + return s +end + +-- these are allowed within a path segment, along with alphanum +-- other characters must be escaped +local segment_set = make_set { + "-", "_", ".", "!", "~", "*", "'", "(", + ")", ":", "@", "&", "=", "+", "$", ",", +} + +local function protect_segment(s) + return string.gsub(s, "([^A-Za-z0-9_])", function (c) + if segment_set[c] then return c + else return string.format("%%%02X", string.byte(c)) end + end) +end + +----------------------------------------------------------------------------- +-- Unencodes a escaped hexadecimal string into its binary representation +-- Input +-- s: escaped hexadecimal string to be unencoded +-- Returns +-- unescaped binary representation of escaped hexadecimal binary +----------------------------------------------------------------------------- +function _M.unescape(s) + return (string.gsub(s, "%%(%x%x)", function(hex) + return string.char(base.tonumber(hex, 16)) + end)) +end + +----------------------------------------------------------------------------- +-- Removes '..' and '.' components appropriately from a path. +-- Input +-- path +-- Returns +-- dot-normalized path +local function remove_dot_components(path) + local marker = string.char(1) + repeat + local was = path + path = path:gsub('//', '/'..marker..'/', 1) + until path == was + repeat + local was = path + path = path:gsub('/%./', '/', 1) + until path == was + repeat + local was = path + path = path:gsub('[^/]+/%.%./([^/]+)', '%1', 1) + until path == was + path = path:gsub('[^/]+/%.%./*$', '') + path = path:gsub('/%.%.$', '/') + path = path:gsub('/%.$', '/') + path = path:gsub('^/%.%./', '/') + path = path:gsub(marker, '') + return path +end + +----------------------------------------------------------------------------- +-- Builds a path from a base path and a relative path +-- Input +-- base_path +-- relative_path +-- Returns +-- corresponding absolute path +----------------------------------------------------------------------------- +local function absolute_path(base_path, relative_path) + if string.sub(relative_path, 1, 1) == "/" then + return remove_dot_components(relative_path) end + base_path = base_path:gsub("[^/]*$", "") + if not base_path:find'/$' then base_path = base_path .. '/' end + local path = base_path .. relative_path + path = remove_dot_components(path) + return path +end + +----------------------------------------------------------------------------- +-- Parses a url and returns a table with all its parts according to RFC 2396 +-- The following grammar describes the names given to the URL parts +-- ::= :///;?# +-- ::= @: +-- ::= [:] +-- :: = {/} +-- Input +-- url: uniform resource locator of request +-- default: table with default values for each field +-- Returns +-- table with the following fields, where RFC naming conventions have +-- been preserved: +-- scheme, authority, userinfo, user, password, host, port, +-- path, params, query, fragment +-- Obs: +-- the leading '/' in {/} is considered part of +----------------------------------------------------------------------------- +function _M.parse(url, default) + -- initialize default parameters + local parsed = {} + for i,v in base.pairs(default or parsed) do parsed[i] = v end + -- empty url is parsed to nil + if not url or url == "" then return nil, "invalid url" end + -- remove whitespace + -- url = string.gsub(url, "%s", "") + -- get scheme + url = string.gsub(url, "^([%w][%w%+%-%.]*)%:", + function(s) parsed.scheme = s; return "" end) + -- get authority + url = string.gsub(url, "^//([^/]*)", function(n) + parsed.authority = n + return "" + end) + -- get fragment + url = string.gsub(url, "#(.*)$", function(f) + parsed.fragment = f + return "" + end) + -- get query string + url = string.gsub(url, "%?(.*)", function(q) + parsed.query = q + return "" + end) + -- get params + url = string.gsub(url, "%;(.*)", function(p) + parsed.params = p + return "" + end) + -- path is whatever was left + if url ~= "" then parsed.path = url end + local authority = parsed.authority + if not authority then return parsed end + authority = string.gsub(authority,"^([^@]*)@", + function(u) parsed.userinfo = u; return "" end) + authority = string.gsub(authority, ":([^:%]]*)$", + function(p) parsed.port = p; return "" end) + if authority ~= "" then + -- IPv6? + parsed.host = string.match(authority, "^%[(.+)%]$") or authority + end + local userinfo = parsed.userinfo + if not userinfo then return parsed end + userinfo = string.gsub(userinfo, ":([^:]*)$", + function(p) parsed.password = p; return "" end) + parsed.user = userinfo + return parsed +end + +----------------------------------------------------------------------------- +-- Rebuilds a parsed URL from its components. +-- Components are protected if any reserved or unallowed characters are found +-- Input +-- parsed: parsed URL, as returned by parse +-- Returns +-- a stringing with the corresponding URL +----------------------------------------------------------------------------- +function _M.build(parsed) + --local ppath = _M.parse_path(parsed.path or "") + --local url = _M.build_path(ppath) + local url = parsed.path or "" + if parsed.params then url = url .. ";" .. parsed.params end + if parsed.query then url = url .. "?" .. parsed.query end + local authority = parsed.authority + if parsed.host then + authority = parsed.host + if string.find(authority, ":") then -- IPv6? + authority = "[" .. authority .. "]" + end + if parsed.port then authority = authority .. ":" .. base.tostring(parsed.port) end + local userinfo = parsed.userinfo + if parsed.user then + userinfo = parsed.user + if parsed.password then + userinfo = userinfo .. ":" .. parsed.password + end + end + if userinfo then authority = userinfo .. "@" .. authority end + end + if authority then url = "//" .. authority .. url end + if parsed.scheme then url = parsed.scheme .. ":" .. url end + if parsed.fragment then url = url .. "#" .. parsed.fragment end + -- url = string.gsub(url, "%s", "") + return url +end + +----------------------------------------------------------------------------- +-- Builds a absolute URL from a base and a relative URL according to RFC 2396 +-- Input +-- base_url +-- relative_url +-- Returns +-- corresponding absolute url +----------------------------------------------------------------------------- +function _M.absolute(base_url, relative_url) + local base_parsed + if base.type(base_url) == "table" then + base_parsed = base_url + base_url = _M.build(base_parsed) + else + base_parsed = _M.parse(base_url) + end + local result + local relative_parsed = _M.parse(relative_url) + if not base_parsed then + result = relative_url + elseif not relative_parsed then + result = base_url + elseif relative_parsed.scheme then + result = relative_url + else + relative_parsed.scheme = base_parsed.scheme + if not relative_parsed.authority then + relative_parsed.authority = base_parsed.authority + if not relative_parsed.path then + relative_parsed.path = base_parsed.path + if not relative_parsed.params then + relative_parsed.params = base_parsed.params + if not relative_parsed.query then + relative_parsed.query = base_parsed.query + end + end + else + relative_parsed.path = absolute_path(base_parsed.path or "", + relative_parsed.path) + end + end + result = _M.build(relative_parsed) + end + return remove_dot_components(result) +end + +----------------------------------------------------------------------------- +-- Breaks a path into its segments, unescaping the segments +-- Input +-- path +-- Returns +-- segment: a table with one entry per segment +----------------------------------------------------------------------------- +function _M.parse_path(path) + local parsed = {} + path = path or "" + --path = string.gsub(path, "%s", "") + string.gsub(path, "([^/]+)", function (s) table.insert(parsed, s) end) + for i = 1, #parsed do + parsed[i] = _M.unescape(parsed[i]) + end + if string.sub(path, 1, 1) == "/" then parsed.is_absolute = 1 end + if string.sub(path, -1, -1) == "/" then parsed.is_directory = 1 end + return parsed +end + +----------------------------------------------------------------------------- +-- Builds a path component from its segments, escaping protected characters. +-- Input +-- parsed: path segments +-- unsafe: if true, segments are not protected before path is built +-- Returns +-- path: corresponding path stringing +----------------------------------------------------------------------------- +function _M.build_path(parsed, unsafe) + local path = "" + local n = #parsed + if unsafe then + for i = 1, n-1 do + path = path .. parsed[i] + path = path .. "/" + end + if n > 0 then + path = path .. parsed[n] + if parsed.is_directory then path = path .. "/" end + end + else + for i = 1, n-1 do + path = path .. protect_segment(parsed[i]) + path = path .. "/" + end + if n > 0 then + path = path .. protect_segment(parsed[n]) + if parsed.is_directory then path = path .. "/" end + end + end + if parsed.is_absolute then path = "/" .. path end + return path +end + +return _M diff --git a/V2RayGCon/Resources/Files/lua/modules/httpApiServ.lua b/V2RayGCon/Resources/Files/lua/modules/httpApiServ.lua new file mode 100644 index 00000000..4813dcaa --- /dev/null +++ b/V2RayGCon/Resources/Files/lua/modules/httpApiServ.lua @@ -0,0 +1,106 @@ +--[[ + +example: + +local url = "http://localhost:4000/" + +local haServ = require('lua.modules.httpApiServ').new() + +local handlers = { + ["hello"] = function() return "world" end, + ["goodbye"] = function() haServ:Close() return "bye" end, +} + +local html = "

index.html

" +haServ:Create(url, html, handlers) + +print("Waiting for connections ...") +haServ:Run() + +--]] + +local M = {} + +local function CreateServer(self) + + local inbox = Sys:ApplyRandomMailBox() + assert(inbox ~= nil, "Apply mail box failed!") + + local outbox = Sys:ApplyRandomMailBox() + assert(outbox ~= nil, "Apply mail box failed!") + + self.inbox = inbox + self.outbox = outbox + self.outadd = outbox:GetAddress() + + local serv = Sys:CreateHttpServer(self.url, self.inbox, self.outbox) + return serv +end + +local function Response(self, title, text) + self.outbox:Send(self.outadd, title, text) +end + +local function HandlePost(self, title, cmd) + local r = "" + if self.handlers[cmd] == nil then + r = "unknow cmd: " .. cmd + else + r = self.handlers[cmd]() + end + + Response(self, title, r) +end + +local function HandleOneConn(self) + + local mail = self.inbox:Wait() + + if mail == nil then + return false + end + + local code = mail:GetCode() + local title = mail:GetTitle() + if code ~= 1 and self.html ~= nil then + Response(self, title, self.html) + else + local cmd = mail:GetContent() + HandlePost(self, title, cmd) + end + + return true +end + +function M:Create(url, html, handlers) + + assert(type(url) == "string", "Param url should be string") + assert(type(handlers) == "table", "Param handlers should be function table.") + assert(type(html) == "string", "Param index should be string.") + + self.url = url + self.html = html + self.handlers = handlers + self.serv = CreateServer(self) + +end + +function M:Close() + self.inbox:Close() +end + +function M:Run() + self.serv:Start() + repeat + local ok = HandleOneConn(self) + until not ok + self.serv:Stop() +end + +function M.new() + local o = {} + setmetatable(o, {__index = M}) + return o +end + +return M \ No newline at end of file diff --git a/V2RayGCon/Services/Notifier.cs b/V2RayGCon/Services/Notifier.cs index d186b2b2..dc44bd8e 100644 --- a/V2RayGCon/Services/Notifier.cs +++ b/V2RayGCon/Services/Notifier.cs @@ -738,27 +738,31 @@ private static void DrawTriangle( { var lw = w * 0.07f; var cr = w * 0.22f; + if (isFirstServ) { - cr -= lw / 2; + cr = cr - lw / 2; } + var dh = Math.Sqrt(3) * cr / 2f; var tri = new Point[] { - new Point((int)(cx - cr / 2f),(int)(cx - dh)), - new Point((int)(cx + cr),(int)cx), - new Point((int)(cx - cr / 2f),(int)(cx + dh)), - }; + new Point((int)(cx - cr / 2f),(int)(cx - dh)), + new Point((int)(cx + cr),(int)cx), + new Point((int)(cx - cr / 2f),(int)(cx + dh)), + }; if (!isFirstServ) { graphics.FillPolygon(Brushes.White, tri); - } - else - { - var pen = new Pen(Brushes.White, lw); - graphics.DrawPolygon(pen, tri); + return; } + var pen = new Pen(Brushes.White, lw) + { + StartCap = LineCap.Round, + EndCap = LineCap.Round, + }; + graphics.DrawPolygon(pen, tri); } private static void DrawOneLine( diff --git a/V2RayGCon/Views/UserControls/SubscriptionUI.cs b/V2RayGCon/Views/UserControls/SubscriptionUI.cs index 2e7ed637..da4eea43 100644 --- a/V2RayGCon/Views/UserControls/SubscriptionUI.cs +++ b/V2RayGCon/Views/UserControls/SubscriptionUI.cs @@ -154,11 +154,22 @@ private void lbAlias_Click(object sender, EventArgs e) private void lbUrl_Click(object sender, EventArgs e) { - tboxUrl.Focus(); - var msg = VgcApis.Misc.Utils.CopyToClipboard(tboxUrl.Text) ? - I18N.CopySuccess : I18N.CopyFail; - VgcApis.Misc.UI.MsgBoxAsync(msg); - + var url = tboxUrl.Text; + if (string.IsNullOrEmpty(url)) + { + var clipboard = VgcApis.Misc.Utils.CopyFromClipboard(); + if (!string.IsNullOrEmpty(clipboard)) + { + tboxUrl.Text = clipboard; + } + } + else + { + tboxUrl.Focus(); + var msg = VgcApis.Misc.Utils.CopyToClipboard(tboxUrl.Text) ? + I18N.CopySuccess : I18N.CopyFail; + VgcApis.Misc.UI.MsgBoxAsync(msg); + } } #endregion diff --git a/VgcApis/Interfaces/Lua/ILuaSys.cs b/VgcApis/Interfaces/Lua/ILuaSys.cs index c4f12a8a..c4e43c4a 100644 --- a/VgcApis/Interfaces/Lua/ILuaSys.cs +++ b/VgcApis/Interfaces/Lua/ILuaSys.cs @@ -5,6 +5,10 @@ namespace VgcApis.Interfaces.Lua { public interface ILuaSys { + #region Net + IRunnable CreateHttpServer(string url, ILuaMailBox inbox, ILuaMailBox outbox); + #endregion + #region keyboard hotkey string GetAllKeyNames(); bool UnregisterHotKey(ILuaMailBox mailbox, string handle); @@ -61,6 +65,12 @@ Process Run(string exePath, string args, string stdin, #endregion #region system + void VolumeUp(); + + void VolumeDown(); + + void VolumeMute(); + string GetOsVersion(); string GetOsReleaseInfo(); diff --git a/VgcApis/Interfaces/Lua/ILuaWeb.cs b/VgcApis/Interfaces/Lua/ILuaWeb.cs index 56aa4a44..850c990d 100644 --- a/VgcApis/Interfaces/Lua/ILuaWeb.cs +++ b/VgcApis/Interfaces/Lua/ILuaWeb.cs @@ -26,6 +26,10 @@ public interface ILuaWeb // e.g. url = "http://baidu.com/" href = "/index.html" result = "http://baidu.com/index.html" string PatchHref(string url, string href); + string Post(string url, string text); + + string Post(string url, string text, int timeout); + // using bing.com to search sth. string Search(string keywords, int first, int proxyPort); diff --git a/VgcApis/Interfaces/Lua/IRunnable.cs b/VgcApis/Interfaces/Lua/IRunnable.cs new file mode 100644 index 00000000..b12929b7 --- /dev/null +++ b/VgcApis/Interfaces/Lua/IRunnable.cs @@ -0,0 +1,8 @@ +namespace VgcApis.Interfaces.Lua +{ + public interface IRunnable + { + void Start(); + void Stop(); + } +} diff --git a/VgcApis/Misc/Utils.cs b/VgcApis/Misc/Utils.cs index 564ad4db..fcb049cb 100644 --- a/VgcApis/Misc/Utils.cs +++ b/VgcApis/Misc/Utils.cs @@ -21,7 +21,7 @@ namespace VgcApis.Misc { public static class Utils - { + { #region editor public static string GetWordFromCurPos(Scintilla editor) { @@ -34,6 +34,7 @@ public static string GetWordFromCurPos(Scintilla editor) } var start = editor.CurrentPosition - line.Position - 1; + start = Clamp(start, 0, text.Length); var end = start; for (; start >= 0; start--) { @@ -60,10 +61,10 @@ public static string GetWordFromCurPos(Scintilla editor) } return text.Substring(start + 1, len); - } + } #endregion - - + + #region system public static string GetCurCallStack() { @@ -1293,6 +1294,16 @@ public static string RelativePath2FullPath(string path) return Path.Combine(appDir, path); } + public static string CopyFromClipboard() + { + try + { + return Clipboard.GetText(); + } + catch { } + return string.Empty; + } + public static bool CopyToClipboard(string content) { try diff --git a/VgcApis/Models/Consts/Lua.cs b/VgcApis/Models/Consts/Lua.cs index 4d376995..19236586 100644 --- a/VgcApis/Models/Consts/Lua.cs +++ b/VgcApis/Models/Consts/Lua.cs @@ -59,6 +59,8 @@ static List GetterApiFuncNames() typeof(Interfaces.Lua.ILuaMisc), typeof(Interfaces.Lua.ILuaServer), typeof(Interfaces.Lua.ILuaWeb), + typeof(Interfaces.Lua.ILuaMailBox), + typeof(Interfaces.Lua.ILuaMail), typeof(Interfaces.ICoreServCtrl), typeof(Interfaces.CoreCtrlComponents.IConfiger), typeof(Interfaces.CoreCtrlComponents.ICoreCtrl), diff --git a/VgcApis/VgcApis.csproj b/VgcApis/VgcApis.csproj index 07969f84..b17accf5 100644 --- a/VgcApis/VgcApis.csproj +++ b/VgcApis/VgcApis.csproj @@ -65,6 +65,7 @@ +