From 88ac6ea2616fe66644a802a20a15c00522d2431f Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Tue, 27 Nov 2018 10:12:00 +0100 Subject: [PATCH 1/6] Terminal Input extended: - TAB - ESC --- SSHDebugger/Terminal/clsSSHTerminal.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SSHDebugger/Terminal/clsSSHTerminal.cs b/SSHDebugger/Terminal/clsSSHTerminal.cs index e85445b..e193923 100644 --- a/SSHDebugger/Terminal/clsSSHTerminal.cs +++ b/SSHDebugger/Terminal/clsSSHTerminal.cs @@ -473,6 +473,13 @@ public void ShellSend(Gdk.Key key) case Gdk.Key.BackSpace: charBytes = new byte[] { (byte)'\b' }; break; + case Gdk.Key.KP_Tab: + case Gdk.Key.Tab: + charBytes = new byte[] { (byte)'\t' }; + break; + case Gdk.Key.Escape: + charBytes = new byte[] { 0x1B }; //ESC + break; default: charBytes = Encoding.UTF8.GetBytes(new char[] { key.GetChar() }); break; @@ -527,7 +534,7 @@ public String RequestUserInput(String prompt, char? echoChar=null) break; default: { - var keyValue = (char)Gdk.Keyval.ToUnicode((uint)LastKeyPress); + var keyValue = LastKeyPress.GetChar(); Write(echoChar.HasValue ? echoChar.Value.ToString() : keyValue.ToString()); input += keyValue; } From ded844a952abb1b1c6d6749fc9881695fb95b56b Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Tue, 27 Nov 2018 13:50:08 +0100 Subject: [PATCH 2/6] Integration into Menu "Run" with shortcuts --- SSHDebugger.sln | 2 +- SSHDebugger/Debugger/clsSSHDebuggerEngine.cs | 400 ++++++----- .../Debugger/clsSSHSoftDebuggerSession.cs | 3 +- SSHDebugger/Helpers/MessageHelper.cs | 23 + SSHDebugger/Host/clsHost.cs | 659 ++++++++++-------- SSHDebugger/Properties/Manifest.addin.xml | 14 +- SSHDebugger/SSHDebugger.csproj | 4 +- 7 files changed, 610 insertions(+), 495 deletions(-) create mode 100644 SSHDebugger/Helpers/MessageHelper.cs diff --git a/SSHDebugger.sln b/SSHDebugger.sln index 59be4ac..d478619 100644 --- a/SSHDebugger.sln +++ b/SSHDebugger.sln @@ -29,6 +29,6 @@ Global GlobalSection(NestedProjects) = preSolution EndGlobalSection GlobalSection(MonoDevelopProperties) = preSolution - version = 0.4 + version = 0.5 EndGlobalSection EndGlobal diff --git a/SSHDebugger/Debugger/clsSSHDebuggerEngine.cs b/SSHDebugger/Debugger/clsSSHDebuggerEngine.cs index 250f55b..573b263 100644 --- a/SSHDebugger/Debugger/clsSSHDebuggerEngine.cs +++ b/SSHDebugger/Debugger/clsSSHDebuggerEngine.cs @@ -42,189 +42,227 @@ using System.Threading; using System.Threading.Tasks; using MonoDevelop.Debugger; - +using Newtonsoft.Json; +using System.Text; +using SSHDebugger.Helpers; namespace SSHDebugger { - public class clsSSHDebuggerEngine: DebuggerEngineBackend - { - clsSSHSoftDebuggerSession DebuggerSession = null; - public static List HostsList = new List(); - - clsHost selectedHost = null; - AutoResetEvent termWait = new AutoResetEvent (false); - - public override bool CanDebugCommand (ExecutionCommand cmd) - { - return true; - } - - public bool BuildList() - { - bool addedNew = false; - - foreach (var file in IdeApp.ProjectOperations.CurrentSelectedProject.Files.Where(x => x.Name.EndsWith(".ssh.txt"))) - { - if (!HostsList.Exists(x=>x.ScriptPath == file.FilePath)) - { - new clsHost(file.FilePath); - addedNew=true; - } - } - return addedNew; - } - - public override DebuggerSession CreateSession () - { - DebuggerSession = new clsSSHSoftDebuggerSession (); - return DebuggerSession; - } - - public override DebuggerStartInfo CreateDebuggerStartInfo (ExecutionCommand c) - { - - SoftDebuggerStartInfo dsi = null; - try{ - - //If new host, no host is selected, or ther terminal window is closed - if (BuildList () || selectedHost==null || selectedHost.Terminal==null) - { - //Load any new templates - selectedHost = InvokeSynch (GetDebuggerInfo); //Query user for selected host - } - - if (selectedHost != null) { - - if (selectedHost.Terminal == null) - { - #if VTE - selectedHost.Terminal = new windowTerminalVTE(selectedHost); - #else + public class clsSSHDebuggerEngine : DebuggerEngineBackend + { + clsSSHSoftDebuggerSession DebuggerSession = null; + public static List HostsList = new List(); + + clsHost selectedHost = null; + AutoResetEvent termWait = new AutoResetEvent(false); + + public override bool IsDefaultDebugger(ExecutionCommand cmd) + { + return base.IsDefaultDebugger(cmd); + } + + public override bool CanDebugCommand(ExecutionCommand cmd) + { + return true; + } + + private bool BuildList() + { + bool addedNew = false; + + + Project project = IdeApp.ProjectOperations.CurrentSelectedProject; + + //Find Startup-Project + var solution = (IdeApp.ProjectOperations.CurrentSelectedWorkspaceItem as Solution); + if (solution.StartupItem != null) + project = solution.StartupItem as Project; + + if(project == null) + { + MessageHelper.ShowMessage("SSH Debugger - No Project found", "Cannot start SSH Debugger, because no project found!"); + return addedNew; + } + + foreach (var file in project.Files.Where(x => x.Name.EndsWith(".ssh.txt"))) + { + if (!HostsList.Exists(x => x.ScriptPath == file.FilePath)) + { + new clsHost(project, file.FilePath); + addedNew = true; + } + } + return addedNew; + } + + public override DebuggerSession CreateSession() + { + DebuggerSession = new clsSSHSoftDebuggerSession(); + return DebuggerSession; + } + + public override DebuggerStartInfo CreateDebuggerStartInfo(ExecutionCommand c) + { + + SoftDebuggerStartInfo dsi = null; + try + { + + //If new host, no host is selected, or ther terminal window is closed + if (BuildList() || selectedHost == null || selectedHost.Terminal == null) + { + //Load any new templates + selectedHost = InvokeSynch(GetDebuggerInfo); //Query user for selected host + } + + if (selectedHost != null) + { + + if (selectedHost.Terminal == null) + { +#if VTE + selectedHost.Terminal = new windowTerminalVTE(selectedHost); +#else selectedHost.Terminal = new windowTerminalGTK(selectedHost); - #endif - } - else - { - selectedHost.Terminal.Front(); - } - - var done = new ManualResetEvent (false); - Task.Run (() => { - dsi = selectedHost.ProcessScript (true); - }).ContinueWith ((t) => { - done.Set (); - }); - - while (true) { - Gtk.Application.RunIteration (); - if (done.WaitOne (0)) - break; - } - - } - - if (dsi != null) selectedHost.Terminal.SSH.WriteLine("Starting debugger"); - - return dsi; - } - catch (ThreadAbortException) //User closed terminal (probably) - { - return null; - } - catch (Exception ex) - { - Gtk.Application.Invoke (delegate - { - using (var md = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, "Terminal error "+ex.Message)) - { - md.Run (); - md.Destroy(); - } - }); - return null; - } - - } - - void OpenTerminal() - { - - } - - clsHost GetDebuggerInfo () - { - ResponseType response; - String filepath = null; - clsHost selectedHost = null; - - try { - - using (var dlg = new clsDebuggerOptionsDialog ()) - { - response = (Gtk.ResponseType) dlg.Run(); - if (dlg.SelectedHost!=null) - { - filepath = dlg.SelectedHost.ScriptPath; - selectedHost = dlg.SelectedHost; - } - dlg.Destroy(); - } - - while (GLib.MainContext.Iteration ()); - - if (response == Gtk.ResponseType.Accept) { - - Gtk.Application.Invoke (delegate - { - using (var md = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, "Please add a ssh template file manually to your project")) - { - md.Run (); - md.Destroy(); - } - }); - return null; - } else if (response != Gtk.ResponseType.Ok) - return null; - - var properties = PropertyService.Get ("MonoDevelop.Debugger.Soft.SSHDebug", new Properties ()); - properties.Set ("host", filepath); - - return selectedHost; - } - catch(Exception ex) { - Gtk.Application.Invoke (delegate { - using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, ex.Message)) { - md.Title = "SoftDebuggerStartInfo"; - md.Run (); - md.Destroy(); - } - }); - return null; - } - } - - static T InvokeSynch (Func func) - { - - if (MonoDevelop.Core.Runtime.IsMainThread) - return func (); - - var ev = new System.Threading.ManualResetEvent (false); - T val = default (T); - Exception caught = null; - Gtk.Application.Invoke (delegate { - try { - val = func (); - } catch (Exception ex) { - caught = ex; - } finally { - ev.Set (); - } - }); - ev.WaitOne (); - if (caught != null) - throw caught; - return val; - } - } +#endif + } + else + { + selectedHost.Terminal.Front(); + } + + var done = new ManualResetEvent(false); + Task.Run(() => + { + dsi = selectedHost.ProcessScript(true); + }).ContinueWith((t) => + { + done.Set(); + }); + + while (true) + { + Gtk.Application.RunIteration(); + if (done.WaitOne(0)) + break; + } + + } + + if (dsi != null) selectedHost.Terminal.SSH.WriteLine("Starting debugger"); + + return dsi; + } + catch (ThreadAbortException) //User closed terminal (probably) + { + return null; + } + catch (Exception ex) + { + Gtk.Application.Invoke(delegate + { + using (var md = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, "Terminal error: " + ex.Message)) + { + md.Run(); + md.Destroy(); + } + }); + return null; + } + + } + + void OpenTerminal() + { + + } + + clsHost GetDebuggerInfo() + { + ResponseType response; + String filepath = null; + clsHost selectedHost = null; + + try + { + + using (var dlg = new clsDebuggerOptionsDialog()) + { + response = (Gtk.ResponseType)dlg.Run(); + if (dlg.SelectedHost != null) + { + filepath = dlg.SelectedHost.ScriptPath; + selectedHost = dlg.SelectedHost; + } + dlg.Destroy(); + } + + while (GLib.MainContext.Iteration()) ; + + if (response == Gtk.ResponseType.Accept) + { + + Gtk.Application.Invoke(delegate + { + using (var md = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, "Please add a ssh template file manually to your project")) + { + md.Run(); + md.Destroy(); + } + }); + return null; + } + else if (response != Gtk.ResponseType.Ok) + return null; + + var properties = PropertyService.Get("MonoDevelop.Debugger.Soft.SSHDebug", new Properties()); + properties.Set("host", filepath); + + return selectedHost; + } + catch (Exception ex) + { + Gtk.Application.Invoke(delegate + { + using (var md = new MessageDialog(null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, ex.Message)) + { + md.Title = "SoftDebuggerStartInfo"; + md.Run(); + md.Destroy(); + } + }); + return null; + } + } + + static T InvokeSynch(Func func) + { + + if (MonoDevelop.Core.Runtime.IsMainThread) + return func(); + + var ev = new System.Threading.ManualResetEvent(false); + T val = default(T); + Exception caught = null; + Gtk.Application.Invoke(delegate + { + try + { + val = func(); + } + catch (Exception ex) + { + caught = ex; + } + finally + { + ev.Set(); + } + }); + ev.WaitOne(); + if (caught != null) + throw caught; + return val; + } + } } diff --git a/SSHDebugger/Debugger/clsSSHSoftDebuggerSession.cs b/SSHDebugger/Debugger/clsSSHSoftDebuggerSession.cs index 37c0fba..cb36346 100644 --- a/SSHDebugger/Debugger/clsSSHSoftDebuggerSession.cs +++ b/SSHDebugger/Debugger/clsSSHSoftDebuggerSession.cs @@ -23,7 +23,7 @@ protected override void OnRun (DebuggerStartInfo startInfo) }catch (Exception ex) { Gtk.Application.Invoke (delegate { using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, ex.Message)) { - md.Title = "CustomSoftDebuggerSession"; + md.Title = "SSH Debugger"; md.Run (); md.Destroy (); } @@ -35,7 +35,6 @@ protected override void OnRun (DebuggerStartInfo startInfo) protected override void EndSession () { base.EndSession (); - } } diff --git a/SSHDebugger/Helpers/MessageHelper.cs b/SSHDebugger/Helpers/MessageHelper.cs new file mode 100644 index 0000000..5145ed4 --- /dev/null +++ b/SSHDebugger/Helpers/MessageHelper.cs @@ -0,0 +1,23 @@ +using System; +using Gtk; +using MonoDevelop.Ide; + +namespace SSHDebugger.Helpers +{ + public static class MessageHelper + { + public static void ShowMessage(string title, string message) + { + Gtk.Application.Invoke(delegate + { + using (var md = new MessageDialog(IdeApp.Workbench.RootWindow, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, "")) + { + md.Title = title; + md.Text = message; + md.Run(); + md.Destroy(); + } + }); + } + } +} diff --git a/SSHDebugger/Host/clsHost.cs b/SSHDebugger/Host/clsHost.cs index 875548c..115ca27 100644 --- a/SSHDebugger/Host/clsHost.cs +++ b/SSHDebugger/Host/clsHost.cs @@ -2,7 +2,7 @@ // clsHost.cs // // Author: -// Stuart Johnson +// Stuart Johnson // // Copyright (c) 2015 Stuart Johnson, Logic Ethos Ltd. // @@ -29,275 +29,316 @@ using Mono.Debugging.Soft; using Gtk; using MonoDevelop.Ide; -using MonoDevelop.Core; using System.Net; using MonoDevelop.Projects; using System.Linq; using System.Text; -using System.Threading.Tasks; -using System.Threading; using System.Diagnostics; +using MonoDevelop.Components.Commands; +using MonoDevelop.Debugger; +using MonoDevelop.Core.Execution; +using System.Threading; namespace SSHDebugger { - public class clsHost : IDisposable - { - - - - public String LocalHost { get; private set;} - public UInt32 LocalTunnelPort { get; private set;} - public UInt32 RemoteTunnelPort { get; private set;} - - public String Name { get; private set;} - public int RemoteSSHPort { get; private set;} - public String ScriptPath { get; private set;} - - - public String Username { get; private set;} - public String Password { get; set;} - public String RemoteHost { get; private set;} - - public String WorkingDir { get; private set;} - - public String build_exe_path { get; private set;} - - public String TerminalEmulation { get; private set;} - public String TerminalFont { get; private set;} - public int TerminalRows { get; private set;} - public int TerminalCols { get; private set;} - - - ITerminal _terminal = null; - public ITerminal Terminal - { - get{ return _terminal;} - set{ - Password = ""; //Reset password - _terminal = value; - } - } - - - String _hostString; - public String HostString - { - get { return _hostString;} - - private set - { - _hostString = value; - - var pt1 = value.IndexOf ('@'); - var pt2 = value.IndexOf (':'); - if (pt1 > -1) Username = value.Substring (0, pt1); - if (pt2 > -1 && pt2 < pt1) { //password included in url - var userSplit = Username.Split (new char[]{ ':' }, 2); - Username = userSplit[0]; - Password = userSplit[1]; - pt2 = value.IndexOf (':',pt1); - } - - if (pt2 > -1) { - RemoteSSHPort = int.Parse (value.Substring (pt2 + 1, value.Length - pt2 - 1)); - } else { - RemoteSSHPort = 22; - pt2 = value.Length; - } - RemoteHost = value.Substring (pt1+1, pt2 - pt1 -1); - } - } - - - - public clsHost (String filePath) - { - var buildTarget = MonoDevelop.Ide.IdeApp.ProjectOperations.CurrentSelectedBuildTarget; - var buildConfigs = ((DotNetProject)buildTarget).Configurations; - build_exe_path = buildConfigs.Cast ().First (x => x.DebugType == "full").CompiledOutputName; - - ScriptPath = filePath; - LocalHost = IPAddress.Loopback.ToString (); - LocalTunnelPort = 10123; - - TerminalFont = "Monospace 10"; - TerminalCols = 120; - TerminalRows = 50; - TerminalEmulation = "vt100"; - - try - { - ProcessScript (false); - clsSSHDebuggerEngine.HostsList.Add (this); - } - catch (Exception ex) - { - Gtk.Application.Invoke (delegate { - using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok,ex.Message)) { - md.Title = "ProcessScript"; - md.Run (); - md.Destroy (); - } - }); - } - - } - - - public SoftDebuggerStartInfo ProcessScript(bool Execute) - { - - int ConsolePort = -1; - int LineCount = 0; - - try { - - if (Terminal != null) - { - Terminal.SSH.WriteLine("Running script: {0}",Path.GetFileName(ScriptPath)); - Terminal.DebuggerThread = Thread.CurrentThread; - } - - using (var fs = File.OpenText (ScriptPath)) { - String linein; - while ((linein = fs.ReadLine ()) != null) { - LineCount++; - linein = ReplaceVarsInString(linein.Trim ()); - if (linein == "" || linein.StartsWith ("#") || linein.StartsWith ("//")) - continue; - if (linein.StartsWith ("<")) { - if (Execute) - { - var proc_command = linein.Substring(1).Split(new char[]{' '},2); - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.FileName = proc_command[0]; - if (proc_command.Length>1) startInfo.Arguments = proc_command[1]; - Process.Start(startInfo); - } - } else if (linein.StartsWith (">")) { - if (Execute) - if (!Terminal.SSH.Execute(linein.Substring(1))) return null; - } else if (linein.StartsWith ("&>")) { - if (Execute) - if (!Terminal.SSH.ExecuteAsync(linein.Substring(2))) return null; - } else if (linein.StartsWith ("s>") || linein.StartsWith ("S>")) { - if (Execute) - if (!Terminal.SSH.ShellExecute(linein.Substring(2), TimeSpan.FromSeconds(5))) return null; - } else { - var commandLine = linein.Split (new char[]{ ' ', '=' }, 2); - var command = commandLine [0].Trim (); - String commandArgs = ""; - if (commandLine.Length > 1) { - commandArgs = commandLine [1].Trim (); - if (commandArgs.StartsWith ("=")) - commandArgs = commandArgs.Substring (1).TrimStart (); - } - - switch (command.ToLower ()) { - case "host": - HostString = commandArgs; - break; - case "name": - Name = commandArgs; - break; - case "consoleport": - ConsolePort = int.Parse(commandArgs); - break; - case "localhost": - LocalHost = commandArgs; - break; - case "localtunnelport": - LocalTunnelPort = UInt32.Parse(commandArgs); - break; - case "remotetunnelport": - RemoteTunnelPort = UInt32.Parse(commandArgs); - break; - case "workingdir": - case "workingdirectory": - WorkingDir = commandArgs; - break; - case "terminalfont": - TerminalFont = commandArgs; - break; - case "terminalrows": - TerminalRows = int.Parse(commandArgs); - break; - case "terminalcols": - TerminalCols = int.Parse(commandArgs); - break; - case "terminalemulation": - TerminalEmulation = commandArgs; - break; - case "privatekeyfile": - if (!String.IsNullOrEmpty(commandArgs)) Terminal.SSH.AddPrivateKeyFile(commandArgs); - break; - default: - { - if (Execute) - { - switch (command.ToLower ()) - { - case "scp-copy": // $exe-file $mdb-file - foreach (var file in commandArgs.Split(new char[]{' '})) - { - if (!Terminal.SSH.UploadFile(file)) return null; - } - break; - case "scp-sync": - if (!Terminal.SSH.SynchronizeDir(Path.GetDirectoryName(build_exe_path))) return null; - break; - case "starttunnel": - if (!Terminal.SSH.StartTunnel(LocalTunnelPort,RemoteTunnelPort)) return null; - break; - case "sleep": - Thread.Sleep(int.Parse(commandArgs)); - break; - default: - if (Terminal != null) Terminal.SSH.WriteLine ("Script Error (Line {0}): {1} Unkown command", LineCount, linein); - break; - } - } - } - break; - } - } - } - } - if (Execute) return DebuggerInfo(ConsolePort); - } catch (Exception ex) { - String errorMsg = String.Format("SSH Script ended (Line {0}:{1})", LineCount, ex.Message); - if (Terminal != null) { - Terminal.SSH.WriteLine (errorMsg); - } else { - throw new Exception(errorMsg); - } - } - finally { - - } - return null; - } - - String ReplaceVarsInString (String input) - { - var sb = new StringBuilder (); - int pt0 = 0; - int pt1,pt2; - - while ((pt1 = input.IndexOf ("$[",pt0)) != -1) - { - pt2 = input.IndexOf ("]", pt1); - sb.Append (input.Substring (pt0, pt1-pt0)); - pt0 = pt2 + 1; - sb.Append (GetVar(input.Substring(pt1 + 2, pt2 - pt1 - 2))); - } - - if (pt0 == 0) return input; - if (pt0 < input.Length-1) sb.Append (input.Substring (pt0)); - return sb.ToString (); - } - - String GetVar(String input) + public class SSHDebugExecutionCommand : DotNetExecutionCommand + { + + } + //https://github.com/mono/monodevelop/blob/master/main/src/addins/MonoDevelop.Debugger/MonoDevelop.Debugger/DebugCommands.cs + public class SSHDebuggerHandler:CommandHandler + { + internal static IBuildTarget GetRunTarget() + { + return IdeApp.ProjectOperations.CurrentSelectedSolution ?? IdeApp.ProjectOperations.CurrentSelectedBuildTarget; + } + + protected override void Run() + { + if (IdeApp.Workspace.IsOpen) + { + var target = GetRunTarget(); + if (target != null) + { + IdeApp.ProjectOperations.Build(target); //Before debugging => Compile + var console = IdeApp.Workbench.ProgressMonitors.ConsoleFactory.CreateConsole(); + var debuggerEngines = DebuggingService.GetDebuggerEngines().First(w => w.Id=="SSHDebugger"); + DebuggingService.Run(new SSHDebugExecutionCommand(), console, debuggerEngines); + } + } + + base.Run(); + } + + protected override void Update(CommandInfo info) + { + if (!IdeApp.Workspace.IsOpen || !DebuggingService.IsDebuggingSupported) + { + info.Enabled = false; + return; + } + if (DebuggingService.IsDebugging || DebuggingService.IsPaused || DebuggingService.IsRunning) + { + info.Enabled = false; + return; + } + base.Update(info); + } + } + + public class clsHost : IDisposable + { + public String LocalHost { get; private set;} + public UInt32 LocalTunnelPort { get; private set;} + public UInt32 RemoteTunnelPort { get; private set;} + + public String Name { get; private set;} + public int RemoteSSHPort { get; private set;} + public String ScriptPath { get; private set;} + + public String Username { get; private set;} + public String Password { get; set;} + public String RemoteHost { get; private set;} + + public String WorkingDir { get; private set;} + + public String build_exe_path { get; private set;} + + public String TerminalEmulation { get; private set;} + public String TerminalFont { get; private set;} + public int TerminalRows { get; private set;} + public int TerminalCols { get; private set;} + + + ITerminal _terminal = null; + public ITerminal Terminal + { + get{ return _terminal;} + set{ + Password = ""; //Reset password + _terminal = value; + } + } + + + String _hostString; + public String HostString + { + get { return _hostString;} + + private set + { + _hostString = value; + + var pt1 = value.IndexOf ('@'); + var pt2 = value.IndexOf (':'); + if (pt1 > -1) Username = value.Substring (0, pt1); + if (pt2 > -1 && pt2 < pt1) { //password included in url + var userSplit = Username.Split (new char[]{ ':' }, 2); + Username = userSplit[0]; + Password = userSplit[1]; + pt2 = value.IndexOf (':',pt1); + } + + if (pt2 > -1) { + RemoteSSHPort = int.Parse (value.Substring (pt2 + 1, value.Length - pt2 - 1)); + } else { + RemoteSSHPort = 22; + pt2 = value.Length; + } + RemoteHost = value.Substring (pt1+1, pt2 - pt1 -1); + } + } + + + + public clsHost (Project project, String filePath) + { + var buildConfigs = project.Configurations; + build_exe_path = buildConfigs.Cast().First (x => x.DebugType == "full").CompiledOutputName; + + ScriptPath = filePath; + LocalHost = IPAddress.Loopback.ToString (); + LocalTunnelPort = 10123; + + TerminalFont = "Monospace 10"; + TerminalCols = 120; + TerminalRows = 50; + TerminalEmulation = "vt100"; + + try + { + ProcessScript (false); + clsSSHDebuggerEngine.HostsList.Add (this); + } + catch (Exception ex) + { + Gtk.Application.Invoke (delegate { + using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok,ex.Message)) { + md.Title = "ProcessScript"; + md.Run (); + md.Destroy (); + } + }); + } + + } + + + public SoftDebuggerStartInfo ProcessScript(bool Execute) + { + + int ConsolePort = -1; + int LineCount = 0; + + try { + + if (Terminal != null) + { + Terminal.SSH.WriteLine("Running script: {0}",Path.GetFileName(ScriptPath)); + Terminal.DebuggerThread = Thread.CurrentThread; + } + + using (var fs = File.OpenText (ScriptPath)) { + String linein; + while ((linein = fs.ReadLine ()) != null) { + LineCount++; + linein = ReplaceVarsInString(linein.Trim ()); + if (linein == "" || linein.StartsWith ("#") || linein.StartsWith ("//")) + continue; + if (linein.StartsWith ("<")) { + if (Execute) + { + var proc_command = linein.Substring(1).Split(new char[]{' '},2); + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName = proc_command[0]; + if (proc_command.Length>1) startInfo.Arguments = proc_command[1]; + Process.Start(startInfo); + } + } else if (linein.StartsWith (">")) { + if (Execute) + if (!Terminal.SSH.Execute(linein.Substring(1))) return null; + } else if (linein.StartsWith ("&>")) { + if (Execute) + if (!Terminal.SSH.ExecuteAsync(linein.Substring(2))) return null; + } else if (linein.StartsWith ("s>") || linein.StartsWith ("S>")) { + if (Execute) + if (!Terminal.SSH.ShellExecute(linein.Substring(2), TimeSpan.FromSeconds(5))) return null; + } else { + var commandLine = linein.Split (new char[]{ ' ', '=' }, 2); + var command = commandLine [0].Trim (); + String commandArgs = ""; + if (commandLine.Length > 1) { + commandArgs = commandLine [1].Trim (); + if (commandArgs.StartsWith ("=")) + commandArgs = commandArgs.Substring (1).TrimStart (); + } + + switch (command.ToLower ()) { + case "host": + HostString = commandArgs; + break; + case "name": + Name = commandArgs; + break; + case "consoleport": + ConsolePort = int.Parse(commandArgs); + break; + case "localhost": + LocalHost = commandArgs; + break; + case "localtunnelport": + LocalTunnelPort = UInt32.Parse(commandArgs); + break; + case "remotetunnelport": + RemoteTunnelPort = UInt32.Parse(commandArgs); + break; + case "workingdir": + case "workingdirectory": + WorkingDir = commandArgs; + break; + case "terminalfont": + TerminalFont = commandArgs; + break; + case "terminalrows": + TerminalRows = int.Parse(commandArgs); + break; + case "terminalcols": + TerminalCols = int.Parse(commandArgs); + break; + case "terminalemulation": + TerminalEmulation = commandArgs; + break; + case "privatekeyfile": + if (!String.IsNullOrEmpty(commandArgs)) Terminal.SSH.AddPrivateKeyFile(commandArgs); + break; + default: + { + if (Execute) + { + switch (command.ToLower ()) + { + case "scp-copy": // $exe-file $mdb-file + foreach (var file in commandArgs.Split(new char[]{' '})) + { + if (!Terminal.SSH.UploadFile(file)) return null; + } + break; + case "scp-sync": + if (!Terminal.SSH.SynchronizeDir(Path.GetDirectoryName(build_exe_path))) return null; + break; + case "starttunnel": + if (!Terminal.SSH.StartTunnel(LocalTunnelPort,RemoteTunnelPort)) return null; + break; + case "sleep": + Thread.Sleep(int.Parse(commandArgs)); + break; + default: + if (Terminal != null) Terminal.SSH.WriteLine ("Script Error (Line {0}): {1} Unkown command", LineCount, linein); + break; + } + } + } + break; + } + } + } + } + if (Execute) return DebuggerInfo(ConsolePort); + } catch (Exception ex) { + String errorMsg = String.Format("SSH Script ended (Line {0}:{1})", LineCount, ex.Message); + if (Terminal != null) { + Terminal.SSH.WriteLine (errorMsg); + } else { + throw new Exception(errorMsg); + } + } + finally { + + } + return null; + } + + String ReplaceVarsInString (String input) + { + var sb = new StringBuilder (); + int pt0 = 0; + int pt1,pt2; + + while ((pt1 = input.IndexOf ("$[",pt0)) != -1) + { + pt2 = input.IndexOf ("]", pt1); + sb.Append (input.Substring (pt0, pt1-pt0)); + pt0 = pt2 + 1; + sb.Append (GetVar(input.Substring(pt1 + 2, pt2 - pt1 - 2))); + } + + if (pt0 == 0) return input; + if (pt0 < input.Length-1) sb.Append (input.Substring (pt0)); + return sb.ToString (); + } + + String GetVar(String input) { switch (input) { @@ -323,52 +364,52 @@ String GetVar(String input) } - public SoftDebuggerStartInfo DebuggerInfo (int consolePort = -1) - { - try - { - - IPAddress[] addresslist = Dns.GetHostAddresses(LocalHost); - - var startArgs = new SoftDebuggerConnectArgs ("", addresslist[0], (int)LocalTunnelPort, consolePort) { - //infinite connection retries (user can cancel), 800ms between them - TimeBetweenConnectionAttempts = 800, - MaxConnectionAttempts = -1, - }; - - var dsi = new SoftDebuggerStartInfo (startArgs) { - Command = "", - Arguments = "" - }; - - if (Terminal != null) Terminal.SSH.WriteLine ("Configuring debugger {0}:{1}",addresslist[0], (int)LocalTunnelPort); - - return dsi; - - } - catch (Exception ex) - { - - if (Terminal != null) { - Terminal.SSH.WriteLine ("SoftDebuggerStartInfo Error {0}", ex.Message); - } else { - Gtk.Application.Invoke (delegate { - using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, String.Format("SoftDebuggerStartInfo Error {0}", ex.Message))) { - md.Title = "ProcessScript"; - md.Run (); - md.Destroy (); - } - }); - } - return null; - } - } - - public void Dispose() - { - if (Terminal!=null) Terminal.Dispose(); - } - - } + public SoftDebuggerStartInfo DebuggerInfo (int consolePort = -1) + { + try + { + + IPAddress[] addresslist = Dns.GetHostAddresses(LocalHost); + + var startArgs = new SoftDebuggerConnectArgs ("", addresslist[0], (int)LocalTunnelPort, consolePort) { + //infinite connection retries (user can cancel), 800ms between them + TimeBetweenConnectionAttempts = 800, + MaxConnectionAttempts = -1, + }; + + var dsi = new SoftDebuggerStartInfo (startArgs) { + Command = "", + Arguments = "" + }; + + if (Terminal != null) Terminal.SSH.WriteLine ("Configuring debugger {0}:{1}",addresslist[0], (int)LocalTunnelPort); + + return dsi; + + } + catch (Exception ex) + { + + if (Terminal != null) { + Terminal.SSH.WriteLine ("SoftDebuggerStartInfo Error {0}", ex.Message); + } else { + Gtk.Application.Invoke (delegate { + using (var md = new MessageDialog (null, DialogFlags.Modal, MessageType.Info, ButtonsType.Ok, String.Format("SoftDebuggerStartInfo Error {0}", ex.Message))) { + md.Title = "ProcessScript"; + md.Run (); + md.Destroy (); + } + }); + } + return null; + } + } + + public void Dispose() + { + if (Terminal!=null) Terminal.Dispose(); + } + + } } diff --git a/SSHDebugger/Properties/Manifest.addin.xml b/SSHDebugger/Properties/Manifest.addin.xml index f208166..c423633 100644 --- a/SSHDebugger/Properties/Manifest.addin.xml +++ b/SSHDebugger/Properties/Manifest.addin.xml @@ -1,7 +1,7 @@  + version="0.5">
SSH Debugger (VTE Xterm version) Debugging @@ -29,5 +29,17 @@ + + + + + + + \ No newline at end of file diff --git a/SSHDebugger/SSHDebugger.csproj b/SSHDebugger/SSHDebugger.csproj index e4ba724..9197bd3 100644 --- a/SSHDebugger/SSHDebugger.csproj +++ b/SSHDebugger/SSHDebugger.csproj @@ -10,7 +10,7 @@ SSHDebugger v4.6.1 SSHDebugger - 0.4 + 0.5 true @@ -58,6 +58,7 @@ + @@ -80,6 +81,7 @@ + From 617a9c152c3aca47704ad2cc69bf99e97c3fe790 Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Tue, 27 Nov 2018 13:56:33 +0100 Subject: [PATCH 3/6] Image MenuIntegrated added --- MenuIntegrated.png | Bin 0 -> 27558 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 MenuIntegrated.png diff --git a/MenuIntegrated.png b/MenuIntegrated.png new file mode 100644 index 0000000000000000000000000000000000000000..3d055bafe2a0518f9c179b1b4372158ab83501d3 GIT binary patch literal 27558 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 literal 0 HcmV?d00001 From d9f557859cb7da57394597ba48a2433269ef95e4 Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Tue, 27 Nov 2018 14:01:59 +0100 Subject: [PATCH 4/6] Update README.md --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 3a30ff8..878b54d 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,10 @@ Includes a full XTerm console, on the Linux version. Standard text window on Win ![alt tag](https://raw.githubusercontent.com/logicethos/SSHDebugger/master/SSHDebugger.png) + +Easy to run the SSH Debugger from menu "Run" or with shortcut +![alt tag](https://raw.githubusercontent.com/Int32Overflow/SSHDebugger/NiceIntegration/MenuIntegrated.png) + Uses: * Develop .NET apps for embedded devices and small computers such as the Raspberry Pi. From 0aa4da09c030e22d11c3cc411902549e82374870 Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Tue, 27 Nov 2018 14:21:12 +0100 Subject: [PATCH 5/6] Script to generate AddIn-Package "mpack" --- generateMPack-VTE.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100755 generateMPack-VTE.sh diff --git a/generateMPack-VTE.sh b/generateMPack-VTE.sh new file mode 100755 index 0000000..ab3a025 --- /dev/null +++ b/generateMPack-VTE.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cd SSHDebugger/bin/Release-VTE/ +mdtool setup pack SSHDebugger.dll From f0f7a27a5a98ba79ad87e55b765cbb22edc61f7c Mon Sep 17 00:00:00 2001 From: Int32Overflow Date: Mon, 3 Dec 2018 12:27:58 +0100 Subject: [PATCH 6/6] Focus Button "Run" on start --- .../Debugger/clsDebuggerOptionsDialog.cs | 132 +++++++++--------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/SSHDebugger/Debugger/clsDebuggerOptionsDialog.cs b/SSHDebugger/Debugger/clsDebuggerOptionsDialog.cs index 08700ef..45fdbb9 100644 --- a/SSHDebugger/Debugger/clsDebuggerOptionsDialog.cs +++ b/SSHDebugger/Debugger/clsDebuggerOptionsDialog.cs @@ -33,68 +33,72 @@ namespace SSHDebugger { - public class clsDebuggerOptionsDialog : Gtk.Dialog - { - public clsHost SelectedHost; - Gtk.Button newButton = new Gtk.Button ("New Host"); - Gtk.Button connectButton = new Gtk.Button ("Run"); - Gtk.ComboBox combo; - - const Gtk.ResponseType connectResponse = Gtk.ResponseType.Ok; - const Gtk.ResponseType newResponse = Gtk.ResponseType.Accept; - - - Properties properties; - - //TODO: dropdown menus for picking string substitutions. also substitutions for port, ip - public clsDebuggerOptionsDialog () : base ( - "SSH Debug", MonoDevelop.Ide.MessageService.RootWindow, - Gtk.DialogFlags.DestroyWithParent | Gtk.DialogFlags.Modal) - { - properties = PropertyService.Get ("MonoDevelop.Debugger.Soft.SSHDebug", new Properties()); - - AddActionWidget (connectButton, connectResponse); - AddActionWidget (newButton, newResponse); - AddActionWidget (new Gtk.Button (Gtk.Stock.Cancel), Gtk.ResponseType.Cancel); - - var table = new Gtk.Table (1, 2, false); - table.BorderWidth = 6; - VBox.PackStart (table, true, true, 0); - - table.Attach (new Gtk.Label ("Host") { Xalign = 0 }, 0, 1, 0, 1); - - var values = clsSSHDebuggerEngine.HostsList.Select (x => String.Format ("{0} ({1})", x.Name, System.IO.Path.GetFileName (x.ScriptPath))).ToArray (); - combo = new Gtk.ComboBox (values); - - int row=0; - if (clsSSHDebuggerEngine.HostsList.Count == 0) { - connectButton.Sensitive = false; - } else { - - var lastSelected = clsSSHDebuggerEngine.HostsList.Find (x => x.ScriptPath == properties.Get ("host", "")); - if (lastSelected != null) - { - row = clsSSHDebuggerEngine.HostsList.IndexOf (lastSelected); - if (row == -1) - row = 0; - } - Gtk.TreeIter iter; - combo.Model.IterNthChild (out iter, row); - combo.SetActiveIter (iter); - SelectedHost = clsSSHDebuggerEngine.HostsList [combo.Active]; - - combo.Changed += (object sender, EventArgs e) => - { - SelectedHost = clsSSHDebuggerEngine.HostsList [combo.Active]; - }; - - } - - table.Attach (combo, 1, 2, 0, 1); - - - VBox.ShowAll (); - - } - } + public class clsDebuggerOptionsDialog : Gtk.Dialog + { + public clsHost SelectedHost; + Gtk.Button newButton = new Gtk.Button("New Host"); + Gtk.Button connectButton = new Gtk.Button("Run"); + Gtk.ComboBox combo; + + const Gtk.ResponseType connectResponse = Gtk.ResponseType.Ok; + const Gtk.ResponseType newResponse = Gtk.ResponseType.Accept; + + + Properties properties; + + //TODO: dropdown menus for picking string substitutions. also substitutions for port, ip + public clsDebuggerOptionsDialog() : base( + "SSH Debug", MonoDevelop.Ide.MessageService.RootWindow, + Gtk.DialogFlags.DestroyWithParent | Gtk.DialogFlags.Modal) + { + properties = PropertyService.Get("MonoDevelop.Debugger.Soft.SSHDebug", new Properties()); + + AddActionWidget(connectButton, connectResponse); + AddActionWidget(newButton, newResponse); + AddActionWidget(new Gtk.Button(Gtk.Stock.Cancel), Gtk.ResponseType.Cancel); + + var table = new Gtk.Table(1, 2, false); + table.BorderWidth = 6; + VBox.PackStart(table, true, true, 0); + + table.Attach(new Gtk.Label("Host") { Xalign = 0 }, 0, 1, 0, 1); + + var values = clsSSHDebuggerEngine.HostsList.Select(x => String.Format("{0} ({1})", x.Name, System.IO.Path.GetFileName(x.ScriptPath))).ToArray(); + combo = new Gtk.ComboBox(values); + + int row = 0; + if (clsSSHDebuggerEngine.HostsList.Count == 0) + { + connectButton.Sensitive = false; + } + else + { + + var lastSelected = clsSSHDebuggerEngine.HostsList.Find(x => x.ScriptPath == properties.Get("host", "")); + if (lastSelected != null) + { + row = clsSSHDebuggerEngine.HostsList.IndexOf(lastSelected); + if (row == -1) + row = 0; + } + Gtk.TreeIter iter; + combo.Model.IterNthChild(out iter, row); + combo.SetActiveIter(iter); + SelectedHost = clsSSHDebuggerEngine.HostsList[combo.Active]; + + combo.Changed += (object sender, EventArgs e) => + { + SelectedHost = clsSSHDebuggerEngine.HostsList[combo.Active]; + }; + + } + + table.Attach(combo, 1, 2, 0, 1); + + connectButton.GrabFocus(); + + VBox.ShowAll(); + + } + } } \ No newline at end of file