OpensshHandler.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. // OpenSSH Handler
  2. //
  3. // Douglas Thrift
  4. //
  5. // OpensshHandler.cs
  6. /* Copyright 2013 Douglas Thrift
  7. *
  8. * Licensed under the Apache License, Version 2.0 (the "License");
  9. * you may not use this file except in compliance with the License.
  10. * You may obtain a copy of the License at
  11. *
  12. * http://www.apache.org/licenses/LICENSE-2.0
  13. *
  14. * Unless required by applicable law or agreed to in writing, software
  15. * distributed under the License is distributed on an "AS IS" BASIS,
  16. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. * See the License for the specific language governing permissions and
  18. * limitations under the License.
  19. */
  20. using System;
  21. using System.Collections.Generic;
  22. using System.Diagnostics;
  23. using System.IO;
  24. using System.Linq;
  25. using System.Text;
  26. using System.Text.RegularExpressions;
  27. using Microsoft.Win32;
  28. public class OpensshHandler : AbstractHandler, Handler
  29. {
  30. private Regex regex = new Regex(@"^(?:/|--?)openssh(?:[:=](?<openssh_path>.*))?$", RegexOptions.IgnoreCase);
  31. private Regex cygwinRegex = new Regex(@"^(?:/|--?)cygwin(?:[:=](?<cygwin_path>.*))?$", RegexOptions.IgnoreCase);
  32. private Regex minttyRegex = new Regex(@"^(?:/|--?)mintty(?:[:=](?<mintty_path>.*))?$", RegexOptions.IgnoreCase);
  33. private Regex bashRegex = new Regex(@"^(?:/|--?)bash(?:[:=](?<bash_path>.*))?$", RegexOptions.IgnoreCase);
  34. private AutoYesNoOption cygwin = AutoYesNoOption.Auto;
  35. private AutoYesNoOption mintty = AutoYesNoOption.Auto;
  36. private bool bash = false;
  37. private string path = null;
  38. string cygwinPath = null;
  39. string minttyPath = null;
  40. string bashPath = null;
  41. public IList<string> Usages
  42. {
  43. get
  44. {
  45. return new string[]
  46. {
  47. "/openssh[:<openssh-path>] -- Use OpenSSH to connect",
  48. "/cygwin[:(yes|no|<cygwin-path>)] -- Use Cygwin for OpenSSH (by default, Cygwin will be used for OpenSSH if detected)",
  49. "/mintty[:(yes|no|<mintty-path>)] -- Use MinTTY for OpenSSH (by default, MinTTY will be used for OpenSSH if detected)",
  50. "/bash[:(yes|no|<bash-path>)] -- Use Bash login shell for use with ssh-agent",
  51. };
  52. }
  53. }
  54. public MatchOption DoMatch(string arg)
  55. {
  56. Match match;
  57. if ((match = regex.Match(arg)).Success)
  58. return SetValue(match, "openssh_path", ref path);
  59. else if ((match = cygwinRegex.Match(arg)).Success)
  60. return SetYesNoValue(match, "cygwin_path", out cygwin, ref cygwinPath);
  61. else if ((match = minttyRegex.Match(arg)).Success)
  62. return SetYesNoValue(match, "mintty_path", out mintty, ref minttyPath);
  63. else if ((match = bashRegex.Match(arg)).Success)
  64. return SetBooleanValue(match, "bash_path", out bash, ref bashPath);
  65. else
  66. return MatchOption.None;
  67. }
  68. public bool Find()
  69. {
  70. if (path != null)
  71. goto Found;
  72. switch (cygwin)
  73. {
  74. case AutoYesNoOption.Auto:
  75. case AutoYesNoOption.Yes:
  76. if (FindCygwin())
  77. goto Found;
  78. break;
  79. }
  80. if ((path = FindInPath("ssh.exe")) != null)
  81. {
  82. Debug.WriteLine("Found OpenSSH in path: {0}", path, null);
  83. goto Found;
  84. }
  85. return false;
  86. Found:
  87. path = path.Trim();
  88. switch (mintty)
  89. {
  90. case AutoYesNoOption.Auto:
  91. case AutoYesNoOption.Yes:
  92. FindMintty();
  93. break;
  94. }
  95. if (bash)
  96. FindBash();
  97. return true;
  98. }
  99. public void Execute(Uri uri, string user, string password)
  100. {
  101. if (!Find())
  102. throw new Exception("Could not find OpenSSH executable.");
  103. if (cygwinPath != null && bash)
  104. {
  105. ProcessStartInfo info = new ProcessStartInfo(Path.Combine(cygwinPath, "bin", "cygpath.exe"), Quote(path));
  106. info.CreateNoWindow = true;
  107. info.RedirectStandardOutput = true;
  108. info.RedirectStandardError = true;
  109. info.UseShellExecute = false;
  110. Process process = Process.Start(info);
  111. string error = process.StandardError.ReadToEnd().Trim();
  112. path = process.StandardOutput.ReadToEnd().Trim();
  113. process.WaitForExit();
  114. if (process.ExitCode != 0)
  115. throw new Exception(error);
  116. }
  117. var command = new List<string>(new string[] { path });
  118. //if (minttyPath != null)
  119. //{
  120. // command = minttyPath;
  121. // if (cygwinPath != null)
  122. // {
  123. // string icon = Path.Combine(cygwinPath, "Cygwin-Terminal.ico");
  124. // if (File.Exists(icon))
  125. // args.AppendFormat("-i \"{0}\" ", icon);
  126. // }
  127. // args.AppendFormat("-e \"{0}\" ", path);
  128. //}
  129. if (password != null)
  130. Debug.WriteLine("Warning: OpenSSH does not support passing a password.");
  131. if (uri.Port != -1)
  132. AddArguments(command, "-p", uri.Port);
  133. AddArguments(command, user != null ? string.Format("{0}@{1}", user, uri.Host) : uri.Host);
  134. if (bash)
  135. {
  136. command = new List<string>(new string[] { bashPath, "-lc", Quote(Command(command)) });
  137. }
  138. //Debug.WriteLine("Running OpenSSH command: {0} {1}", command, args);
  139. //Process.Start(command, args.ToString());
  140. command.ForEach(item => Debug.WriteLine(item));
  141. }
  142. private bool FindCygwin()
  143. {
  144. if (cygwinPath != null)
  145. goto Found;
  146. foreach (var hive in new RegistryHive[] { RegistryHive.CurrentUser, RegistryHive.LocalMachine })
  147. {
  148. var views = new List<RegistryView>(new RegistryView[] { RegistryView.Registry32 });
  149. if (Environment.Is64BitOperatingSystem)
  150. views.Insert(0, RegistryView.Registry64);
  151. foreach (RegistryView view in views)
  152. using (RegistryKey baseKey = RegistryKey.OpenBaseKey(hive, view), key = baseKey.OpenSubKey(@"SOFTWARE\Cygwin\setup"))
  153. if (key != null)
  154. {
  155. cygwinPath = (string)key.GetValue("rootdir");
  156. if (cygwinPath == null)
  157. continue;
  158. if (Directory.Exists(cygwinPath))
  159. {
  160. Debug.WriteLine("Found Cygwin in registry: {0}", cygwinPath, null);
  161. goto Found;
  162. }
  163. else
  164. cygwinPath = null;
  165. }
  166. }
  167. if (cygwin == AutoYesNoOption.Yes)
  168. throw new Exception("Could not find Cygwin in registry.");
  169. return false;
  170. Found:
  171. cygwinPath = cygwinPath.Trim();
  172. path = Path.Combine(cygwinPath, "bin", "ssh.exe");
  173. if (File.Exists(path))
  174. {
  175. Debug.WriteLine("Found OpenSSH in Cygwin directory: {0}", path, null);
  176. return true;
  177. }
  178. else if (cygwin == AutoYesNoOption.Yes)
  179. throw new Exception("Could not find OpenSSH in Cygwin directory.");
  180. else
  181. {
  182. path = null;
  183. return false;
  184. }
  185. }
  186. private void FindMintty()
  187. {
  188. if (minttyPath != null)
  189. goto Found;
  190. if (cygwinPath != null)
  191. {
  192. minttyPath = Path.Combine(cygwinPath, "bin", "mintty.exe");
  193. if (File.Exists(minttyPath))
  194. {
  195. Debug.WriteLine("Found MinTTY in Cygwin directory: {0}", minttyPath, null);
  196. goto Found;
  197. }
  198. else
  199. minttyPath = null;
  200. }
  201. if ((minttyPath = FindInPath("mintty.exe")) != null)
  202. {
  203. Debug.WriteLine("Found MinTTY in path: {0}", minttyPath, null);
  204. goto Found;
  205. }
  206. if (mintty == AutoYesNoOption.Yes)
  207. throw new Exception("Could not find MinTTY executable.");
  208. return;
  209. Found:
  210. minttyPath = minttyPath.Trim();
  211. }
  212. private void FindBash()
  213. {
  214. if (bashPath != null)
  215. goto Found;
  216. if (cygwinPath != null)
  217. {
  218. bashPath = Path.Combine(cygwinPath, "bin", "bash.exe");
  219. if (File.Exists(bashPath))
  220. {
  221. Debug.WriteLine("Found Bash in Cygwin directory: {0}", bashPath, null);
  222. goto Found;
  223. }
  224. else
  225. bashPath = null;
  226. }
  227. if ((bashPath = FindInPath("bash.exe")) != null)
  228. {
  229. Debug.WriteLine("Found Bash in path: {0}", bashPath, null);
  230. goto Found;
  231. }
  232. throw new Exception("Could not find Bash executable.");
  233. Found:
  234. bashPath = bashPath.Trim();
  235. }
  236. private string Quote(string value)
  237. {
  238. return string.Format("'{0}'", value.Replace("\"", "\\\"").Replace('\'', '"'));
  239. }
  240. private string Command(IList<string> command)
  241. {
  242. return string.Join(" ", command.Select(item => Quote(item)));
  243. }
  244. }