-
Notifications
You must be signed in to change notification settings - Fork 21
/
ClassPutty.cs
189 lines (165 loc) · 7.4 KB
/
ClassPutty.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
using System;
using System.IO;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Windows.Forms;
namespace GitForce
{
/// <summary>
/// Manage PuTTY, PLink and PuttyGen utilities.
/// This class should be instantiated on Windows OS only.
/// </summary>
public class ClassPutty
{
private readonly string pathPageant;
private readonly string pathPlink;
private readonly string pathPuttyGen;
private Process procPageant;
/// <summary>
/// Constructor class function, create executables in the temp space
/// </summary>
public ClassPutty()
{
string pathPageantLong = ClassUtils.WriteResourceToFile(Path.GetTempPath(), "pageant.exe", Properties.Resources.pageant);
string pathPlinkLong = ClassUtils.WriteResourceToFile(Path.GetTempPath(), "plink.exe", Properties.Resources.plink);
string pathPuttyGenLong = ClassUtils.WriteResourceToFile(Path.GetTempPath(), "puttygen.exe", Properties.Resources.puttygen);
pathPageant = ClassUtils.GetShortPathName(pathPageantLong);
pathPlink = ClassUtils.GetShortPathName(pathPlinkLong);
pathPuttyGen = ClassUtils.GetShortPathName(pathPuttyGenLong);
ClassUtils.AddEnvar("PLINK_PROTOCOL", "ssh");
ClassUtils.AddEnvar("GIT_SSH", pathPlink);
// Run the daemon process, update keys
RunPageantUpdateKeys();
}
/// <summary>
/// Destructor for the class make sure the executable resources are properly disposed of
/// </summary>
~ClassPutty()
{
// No real harm done if we fail to remove temp files. The next time GitForce is
// run we will reuse the same files, so the temp folder will not grow.
try
{
// Dont attempt to stop/remove Pageant if the user wanted to leave it running
if (Properties.Settings.Default.leavePageant == false)
{
if (procPageant != null)
{
if (!procPageant.HasExited)
{
// Send a notification to Pageant to close
IntPtr winHandle = NativeMethods.FindWindow("PageantSysTray", null);
NativeMethods.SendMessage(winHandle, NativeMethods.WM_COMMAND, NativeMethods.WM_CLOSE, 0);
}
//File.Delete(_pathPageant);
}
}
// Note: We leave these files in to allow secondary application instances to co-exist
//File.Delete(_pathPlink);
//File.Delete(_pathPuttyGen);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
/// <summary>
/// Run the PuTTYgen process and wait until it exits.
/// </summary>
public void RunPuTTYgen()
{
Process.Start(pathPuttyGen).WaitForExit();
}
/// <summary>
/// Run plink program with the given arguments
/// </summary>
public void RunPLink(string args)
{
// Start a console process
Process proc = new Process();
proc.StartInfo.FileName = ClassUtils.GetShellExecCmd();
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
// We need to keep the CMD/SHELL window open, so start the process using
// the CMD/SHELL as the root process and pass it our command to execute
proc.StartInfo.Arguments = string.Format("{0} \"{1}\" {2}",
ClassUtils.GetShellExecFlags(), pathPlink, args);
App.PrintLogMessage(proc.StartInfo.Arguments, MessageType.Command);
proc.Start();
}
/// <summary>
/// Runs the pageant daemon process and loads keys
/// </summary>
public void RunPageantUpdateKeys()
{
// Load list of keys and passphrases to send to the pageant process
List<string> keys = Properties.Settings.Default.PuTTYKeys.
Split((",").ToCharArray(), StringSplitOptions.RemoveEmptyEntries).ToList();
List<string> pfs = GetPassPhrases();
StringBuilder args = new StringBuilder();
// Passphrases must be specified first in the list of arguments
foreach (string s in pfs)
args.Append(" -x \"" + s + "\"");
// Followed by the list of keys to load
foreach (string s in keys)
args.Append(" \"" + s + "\"");
// Run the pageant process if we have any (new) keys or phrases to import,
// or if the pageant process is not running
bool isPageantRunning = Process.GetProcesses().Any(proc => proc.ProcessName == "pageant");
if (args.Length > 0 || isPageantRunning==false)
{
procPageant = new Process();
procPageant.StartInfo.FileName = pathPageant;
procPageant.StartInfo.Arguments = args.ToString();
// TODO: Handle unsuccessful process start
procPageant.Start();
}
}
/// <summary>
/// Returns the list of passphrases in plaintext format
/// </summary>
public List<string> GetPassPhrases()
{
// Base-64 encoded strings, zero-delimited, are read from application settings
List<string> pfs = Properties.Settings.Default.PuTTYPf.
Split(("\0").ToCharArray(),
StringSplitOptions.RemoveEmptyEntries).
ToList();
// Select each string and tramsform it from Base-64 to plaintext
pfs = pfs.Select(
s => Encoding.ASCII.GetString(
Convert.FromBase64String(s))).
ToList();
return pfs;
}
/// <summary>
/// Saves the list of passphrases into application properties
/// </summary>
public void SetPassPhrases(List<string> pfs)
{
// Convert each passphrase into Base-64 encoded string
pfs = pfs.Select(
s => Convert.ToBase64String(
Encoding.ASCII.GetBytes(s))).
ToList();
Properties.Settings.Default.PuTTYPf = String.Join("\0", pfs.ToArray());
}
/// <summary>
/// Initializes SSH connection by running the PLINK using the specified
/// connection parameters. This function blocks until the PLINK returns.
/// </summary>
public void ImportRemoteSshKey(ClassUrl.Url url)
{
// Build the arguments to the PLINK process: port number, user and the host
// Use the default SSH values if the url did not provide them
string args = " -P " + (url.Port > 0 ? url.Port.ToString() : "22") +
" -l " + (string.IsNullOrEmpty(url.User) ? "anonymous" : url.User) +
" " + url.Host;
// plink does everything through its stderr stream
ExecResult result = Exec.Run(pathPlink, args);
App.PrintLogMessage(result.stderr, MessageType.Error);
}
}
}