Skip to content

Commit 7baebd2

Browse files
committed
Implemented SSH, did some cleanup
1 parent 2692f72 commit 7baebd2

File tree

4 files changed

+307
-63
lines changed

4 files changed

+307
-63
lines changed

Background-Terminal/Background-Terminal.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
<PackageReference Include="CoreMeter" Version="1.0.2" />
2222
<PackageReference Include="Hardcodet.NotifyIcon.Wpf.NetCore" Version="1.0.10" />
2323
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
24+
<PackageReference Include="SSH.NET" Version="2016.1.0" />
2425
<PackageReference Include="System.Management" Version="4.7.0" />
2526
</ItemGroup>
2627

Background-Terminal/MainWindow.xaml.cs

Lines changed: 231 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
using CoreMeter;
22
using Newtonsoft.Json;
3+
using Renci.SshNet;
34
using System;
45
using System.Collections.Generic;
56
using System.Collections.ObjectModel;
67
using System.Diagnostics;
78
using System.IO;
9+
using System.Linq;
810
using System.Management;
911
using System.Text.RegularExpressions;
1012
using System.Threading.Tasks;
@@ -18,29 +20,44 @@ namespace Background_Terminal
1820
{
1921
public partial class MainWindow : Window
2022
{
23+
// Static Fields
2124
private static BrushConverter _brushConverter = new BrushConverter();
2225

2326
private static DirectoryInfo _appDataDirectory = new DirectoryInfo(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "BackgroundTerminal"));
2427
private static string _configFile = "config.json";
2528
private static string _configPath = System.IO.Path.Combine(_appDataDirectory.FullName, _configFile);
2629

27-
private CoreMeterUtility _coreMeterUtility;
28-
30+
// TerminalWindow
2931
private TerminalWindow _terminalWindow;
3032

33+
// Main Process
34+
private Process _process;
35+
36+
// CoreMeter
37+
private CoreMeterUtility _coreMeterUtility;
38+
39+
// Settings Container
3140
private BackgroundTerminalSettings _settings;
3241

33-
private Process _process;
42+
// SSH Handling
43+
private SshClient _sshClient;
44+
private bool _sshMode = false;
45+
private string _sshServer = String.Empty;
46+
private string _sshUsername = String.Empty;
47+
private string _sshCurrentDirectory = String.Empty;
3448

49+
// UI List Bindings
3550
private ObservableCollection<string> _terminalData = new ObservableCollection<string>();
3651
public ObservableCollection<NewlineTrigger> NewlineTriggers { get; set; }
3752

38-
private string _processPath;
53+
// Newline State Handling
3954
private string _currentTrigger = null;
4055
private string _newlineString = Environment.NewLine;
4156

57+
// CMD Process ID
4258
private int _cmdProcessId;
4359

60+
// TerminalWindow UI State Handling
4461
private bool _terminalWindowActive = false;
4562
private bool _terminalWindowLocked = true;
4663

@@ -50,12 +67,13 @@ public partial class MainWindow : Window
5067
private Key? _key1 = null;
5168
private Key? _key2 = null;
5269

70+
#region Constructor
5371
public MainWindow()
5472
{
5573
InitializeComponent();
5674

5775
// Create TerminalWindow
58-
_terminalWindow = new TerminalWindow(SendCommand, KillChildren, TerminalWindowUpdate);
76+
_terminalWindow = new TerminalWindow(SendCommand, KillProcess, TerminalWindowUIUpdate);
5977
_terminalWindow.Show();
6078

6179
// Apply changes in terminal data to TerminalWindow
@@ -132,7 +150,46 @@ public MainWindow()
132150

133151
DataContext = this;
134152
}
153+
#endregion
154+
155+
#region General Functions
156+
private void KillProcess()
157+
{
158+
if (_sshMode)
159+
{
160+
_sshClient.Disconnect();
161+
_sshClient.Dispose();
162+
163+
_sshMode = false;
164+
165+
_terminalData.Add("SSH Session Disconnected");
166+
}
167+
else
168+
{
169+
KillChildren();
170+
}
171+
}
172+
173+
private void OutputSSHUsage()
174+
{
175+
_terminalData.Add("Background Terminal manually handles SSH connection. (Ctrl + C to quit)");
176+
_terminalData.Add("Usage: ssh <server>");
177+
_terminalData.Add("Note that SSH.net does not support change directory (cd), so you are required to prefix the " +
178+
"command with a (cd) call to the directory you want to be in. (cd /my/directory && mycommand)");
179+
_terminalData.Add("To get around this, I have implemented automated directory prefixing. If you call (cd) while in SSH mode, it will automatically prefix any " +
180+
"further commands with the directory you previously specified.");
181+
}
182+
183+
private string DirectoryPrefixCommand(string command)
184+
{
185+
if (!_sshCurrentDirectory.Equals(String.Empty))
186+
return "cd " + _sshCurrentDirectory + " && " + command;
187+
188+
return command;
189+
}
190+
#endregion
135191

192+
#region UI State Functions
136193
private void ApplySettingsToTerminalWindow()
137194
{
138195
_terminalWindow.TerminalData_TextBox.FontSize = _settings.FontSize;
@@ -143,10 +200,19 @@ private void ApplySettingsToTerminalWindow()
143200
_terminalWindow.Top = _settings.PosY;
144201
_terminalWindow.Width = _settings.Width;
145202
_terminalWindow.Height = _settings.Height;
203+
}
204+
205+
private void TerminalWindowUIUpdate()
206+
{
207+
PosX_TextBox.Text = _terminalWindow.Left.ToString();
208+
PosY_TextBox.Text = _terminalWindow.Top.ToString();
146209

147-
_terminalWindow.UpdateTerminalDataTextBoxMargin();
210+
Width_TextBox.Text = _terminalWindow.Width.ToString();
211+
Height_TextBox.Text = _terminalWindow.Height.ToString();
148212
}
213+
#endregion
149214

215+
#region Process Helper Functions
150216
private List<Process> GetProcessChildren()
151217
{
152218
List<Process> children = new List<Process>();
@@ -160,6 +226,20 @@ private List<Process> GetProcessChildren()
160226
return children;
161227
}
162228

229+
private void KillChildren()
230+
{
231+
List<Process> children = GetProcessChildren();
232+
233+
foreach (Process child in children)
234+
{
235+
if (!child.Id.Equals(_cmdProcessId))
236+
{
237+
child.Kill();
238+
}
239+
}
240+
}
241+
#endregion
242+
163243
#region Terminal Data Handlers
164244
private async Task<int> RunTerminalProcessAsync()
165245
{
@@ -182,8 +262,8 @@ private async Task<int> RunTerminalProcessAsync()
182262
_process.StartInfo.RedirectStandardError = true;
183263

184264
_process.EnableRaisingEvents = true;
185-
_process.OutputDataReceived += CMD_OutputDataReceived;
186-
_process.ErrorDataReceived += CMD_ErrorDataReceived;
265+
_process.OutputDataReceived += OutputDataReceived;
266+
_process.ErrorDataReceived += ErrorDataReceived;
187267

188268
_process.Exited += new EventHandler((sender, args) =>
189269
{
@@ -216,33 +296,162 @@ private async Task<int> RunTerminalProcessAsync()
216296
return await taskCompletionSource.Task;
217297
}
218298

219-
private void CMD_OutputDataReceived(object sender, DataReceivedEventArgs e)
299+
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
220300
{
221301
_terminalData.Add(e.Data);
222302
}
223303

224-
private void CMD_ErrorDataReceived(object sender, DataReceivedEventArgs e)
304+
private void ErrorDataReceived(object sender, DataReceivedEventArgs e)
225305
{
226306
_terminalData.Add(e.Data);
227307
}
228308

229-
private void SendCommand(string command, bool output = true)
309+
private string SendCommandSSH(string command, bool silent = false)
310+
{
311+
// Handle SSH login connection
312+
if (_sshUsername.Equals(String.Empty))
313+
{
314+
_sshUsername = command;
315+
_terminalData.Add("Enter password:");
316+
317+
_terminalWindow._passwordMode = true;
318+
}
319+
else if (_terminalWindow._passwordMode)
320+
{
321+
_terminalData.Add("Connecting...");
322+
323+
// Attempt connection
324+
_sshClient = new SshClient(_sshServer, _sshUsername, _terminalWindow._password);
325+
try
326+
{
327+
_sshClient.Connect();
328+
329+
_terminalWindow._passwordMode = false;
330+
_terminalWindow._password = String.Empty;
331+
332+
if (_sshClient.IsConnected)
333+
_terminalData.Add("Connected to " + _sshServer);
334+
else
335+
{
336+
_terminalData.Add("There was a problem connecting.");
337+
338+
_sshMode = false;
339+
_sshUsername = String.Empty;
340+
}
341+
}
342+
catch (Exception e)
343+
{
344+
_terminalData.Add(e.Message);
345+
}
346+
}
347+
348+
// Handle SSH commands
349+
else
350+
{
351+
if (_sshClient.IsConnected)
352+
{
353+
try
354+
{
355+
SshCommand sshCommand = _sshClient.CreateCommand(command);
356+
string result = sshCommand.Execute();
357+
358+
StreamReader reader = new StreamReader(sshCommand.ExtendedOutputStream);
359+
string extendedResult = reader.ReadToEnd();
360+
361+
if (result.Length > 0 && (result[result.Length - 1] == '\n' || result[result.Length - 1] == '\r'))
362+
result = result.Substring(0, result.Length - 1);
363+
364+
// Handle silent calls to pwd maintain SSH current directory
365+
if (silent)
366+
return result;
367+
368+
if (extendedResult.Length > 0 && (extendedResult[extendedResult.Length - 1] == '\n' || extendedResult[extendedResult.Length - 1] == '\r'))
369+
extendedResult = extendedResult.Substring(0, extendedResult.Length - 1);
370+
371+
if (!result.Equals(String.Empty))
372+
_terminalData.Add(result);
373+
374+
if (!extendedResult.Equals(String.Empty))
375+
_terminalData.Add(extendedResult);
376+
377+
}
378+
catch (Exception e)
379+
{
380+
_terminalData.Add(e.Message);
381+
}
382+
}
383+
else
384+
{
385+
_terminalData.Add("You are no longer connected to SSH. Exiting.");
386+
387+
_sshMode = false;
388+
_sshUsername = String.Empty;
389+
}
390+
}
391+
392+
return null;
393+
}
394+
395+
private void SendCommandBGT(string command)
230396
{
397+
string bgtCommand = command.Split(' ')[1];
398+
string[] parameters = command.Substring(command.IndexOf(bgtCommand) + bgtCommand.Length + 1).Split(' ');
399+
400+
if (bgtCommand.Equals("newline"))
401+
{
402+
_newlineString = Regex.Unescape(parameters[0]);
403+
}
404+
}
405+
406+
private void SendCommand(string command)
407+
{
408+
409+
410+
// Handle SSH mode
411+
if (_sshMode)
412+
{
413+
_terminalData.Add(DirectoryPrefixCommand(command));
414+
SendCommandSSH(DirectoryPrefixCommand(command));
415+
416+
if (command.ToLower().StartsWith("cd"))
417+
_sshCurrentDirectory = SendCommandSSH(command + " && pwd", true);
418+
}
419+
231420
// Background-Terminal application commands
232-
if (command.StartsWith("bgt"))
421+
else if (command.ToLower().StartsWith("bgt"))
233422
{
234-
string bgtCommand = command.Split(' ')[1];
235-
string[] parameters = command.Substring(command.IndexOf(bgtCommand) + bgtCommand.Length + 1).Split(' ');
423+
_terminalData.Add(command);
424+
SendCommandBGT(command);
425+
}
426+
427+
// Initialize SSH mode
428+
else if (command.ToLower().StartsWith("ssh"))
429+
{
430+
_terminalData.Add(command);
236431

237-
if (bgtCommand.Equals("newline"))
432+
List<string> commandParams = command.Split(' ').ToList();
433+
434+
if (commandParams.Count != 2)
238435
{
239-
_newlineString = Regex.Unescape(parameters[0]);
436+
OutputSSHUsage();
437+
}
438+
else
439+
{
440+
_sshServer = commandParams[1];
441+
442+
OutputSSHUsage();
443+
444+
_terminalData.Add("");
445+
_terminalData.Add("Enter username:");
446+
447+
_sshMode = true;
240448
}
241449
}
450+
451+
// Standard command handling
242452
else
243453
{
244-
if (output)
245-
_terminalData.Add(command);
454+
_terminalData.Add(command);
246455

247456
_process.StandardInput.NewLine = _newlineString;
248457
_process.StandardInput.WriteLine(command);
@@ -265,28 +474,6 @@ private void SendCommand(string command, bool output = true)
265474
}
266475
}
267476
}
268-
269-
private void KillChildren()
270-
{
271-
List<Process> children = GetProcessChildren();
272-
273-
foreach (Process child in children)
274-
{
275-
if (!child.Id.Equals(_cmdProcessId))
276-
{
277-
child.Kill();
278-
}
279-
}
280-
}
281-
282-
private void TerminalWindowUpdate()
283-
{
284-
PosX_TextBox.Text = _terminalWindow.Left.ToString();
285-
PosY_TextBox.Text = _terminalWindow.Top.ToString();
286-
287-
Width_TextBox.Text = _terminalWindow.Width.ToString();
288-
Height_TextBox.Text = _terminalWindow.Height.ToString();
289-
}
290477
#endregion
291478

292479
#region Event Handlers
@@ -484,6 +671,10 @@ private void MinimizeButton_Click(object sender, RoutedEventArgs e)
484671

485672
private void ExitButton_Click(object sender, RoutedEventArgs e)
486673
{
674+
_sshClient.Disconnect();
675+
_sshClient.Dispose();
676+
_process.Kill();
677+
487678
Close();
488679
}
489680

0 commit comments

Comments
 (0)