diff --git a/MenuIntegrated.png b/MenuIntegrated.png new file mode 100644 index 0000000..3d055ba Binary files /dev/null and b/MenuIntegrated.png differ 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. 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/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 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 @@ + 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; } 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