ssh-handler.cs 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. // SSH Handler
  2. //
  3. // Douglas Thrift
  4. //
  5. // ssh-handler.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 System.Windows.Forms;
  28. using Microsoft.Win32;
  29. public enum Option
  30. {
  31. None,
  32. Set,
  33. Optional,
  34. }
  35. public interface Handler
  36. {
  37. IList<string> Usages
  38. {
  39. get;
  40. }
  41. Option DoMatch(string arg);
  42. bool Find();
  43. void Execute(Uri uri, string user, string password);
  44. }
  45. public abstract class FindInPathMixin
  46. {
  47. protected string FindInPath(string program)
  48. {
  49. foreach (string location in Environment.GetEnvironmentVariable("PATH").Split(Path.PathSeparator))
  50. {
  51. string path = Path.Combine(location, "putty.exe");
  52. if (File.Exists(path))
  53. return path;
  54. }
  55. return null;
  56. }
  57. }
  58. public class Putty : FindInPathMixin, Handler
  59. {
  60. private Regex option = new Regex(@"^(?:/|--?)putty(?:[:=](?<putty_path>.*))?$", RegexOptions.IgnoreCase);
  61. private string path = null;
  62. public IList<string> Usages
  63. {
  64. get
  65. {
  66. return new string[] { "/putty[:<putty-path>] -- Use PuTTY to connect" };
  67. }
  68. }
  69. public Option DoMatch(string arg)
  70. {
  71. Match match;
  72. if ((match = option.Match(arg)).Success)
  73. {
  74. Group group = match.Groups["putty_path"];
  75. if (group.Success)
  76. path = group.Value;
  77. return Option.Set;
  78. }
  79. else
  80. return Option.None;
  81. }
  82. public bool Find()
  83. {
  84. if (path != null)
  85. goto found;
  86. foreach (RegistryHive hive in new RegistryHive[] { RegistryHive.CurrentUser, RegistryHive.LocalMachine })
  87. {
  88. IList<RegistryView> views = new List<RegistryView>(new RegistryView[] { RegistryView.Registry32 });
  89. if (Environment.Is64BitOperatingSystem)
  90. views.Insert(0, RegistryView.Registry64);
  91. foreach (RegistryView view in views)
  92. using (RegistryKey baseKey = RegistryKey.OpenBaseKey(hive, view), key = baseKey.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\PuTTY_is1"))
  93. if (key != null)
  94. {
  95. string location = (string)key.GetValue("InstallLocation");
  96. if (location == null)
  97. continue;
  98. path = Path.Combine(location, "putty.exe");
  99. if (File.Exists(path))
  100. {
  101. Debug.WriteLine("Found PuTTY in registry: {0}", path, null);
  102. goto found;
  103. }
  104. else
  105. path = null;
  106. }
  107. }
  108. if ((path = FindInPath("putty.exe")) != null)
  109. {
  110. Debug.WriteLine("Found PuTTY in path: {0}", path, null);
  111. goto found;
  112. }
  113. return false;
  114. found:
  115. path = path.Trim();
  116. return true;
  117. }
  118. public void Execute(Uri uri, string user, string password)
  119. {
  120. if (!Find())
  121. throw new Exception("Could not find PuTTY executable.");
  122. StringBuilder args = new StringBuilder();
  123. if (password != null)
  124. args.AppendFormat("-pw {0} ", password);
  125. if (uri.Port != -1)
  126. args.AppendFormat("-P {0} ", uri.Port);
  127. if (user != null)
  128. args.AppendFormat("{0}@", user);
  129. args.Append(uri.Host);
  130. Debug.WriteLine("Running PuTTY command: {0} {1}", path, args);
  131. Process.Start(path, args.ToString());
  132. }
  133. }
  134. public class SshHandler
  135. {
  136. private static IList<Handler> handlers = new Handler[]
  137. {
  138. new Putty(),
  139. };
  140. private static Handler handler = null;
  141. private static string puttyPath = null;
  142. public static int Main(string[] args)
  143. {
  144. Application.EnableVisualStyles();
  145. try
  146. {
  147. Regex usage = new Regex(@"^(?:/|--?)(?:h|help|usage|\?)$", RegexOptions.IgnoreCase);
  148. IList<string> uriParts = null;
  149. foreach (string arg in args)
  150. if (uriParts == null)
  151. {
  152. if (usage.IsMatch(arg))
  153. return Usage(0);
  154. if (!MatchHandler(arg))
  155. uriParts = new List<string>(new string[] { arg });
  156. }
  157. else
  158. uriParts.Add(arg);
  159. if (uriParts != null)
  160. {
  161. Uri uri = new Uri(string.Join(" ", uriParts), UriKind.Absolute);
  162. string user, password;
  163. SetUserPassword(uri, out user, out password);
  164. if (handler == null)
  165. handler = FindHandler();
  166. handler.Execute(uri, user, password);
  167. }
  168. else
  169. return Usage(1);
  170. }
  171. catch (Exception exception)
  172. {
  173. MessageBox.Show(exception.Message, "SSH Handler Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
  174. return 2;
  175. }
  176. return 0;
  177. }
  178. private static int Usage(int code)
  179. {
  180. MessageBox.Show("ssh-handler [/putty[:<putty-path>]] <ssh-url>\n\n" +
  181. string.Join("\n", handlers.SelectMany(handler => handler.Usages)), "SSH Handler Usage", MessageBoxButtons.OK,
  182. MessageBoxIcon.Information);
  183. return code;
  184. }
  185. private static bool MatchHandler(string arg)
  186. {
  187. foreach (Handler handler in handlers)
  188. switch (handler.DoMatch(arg))
  189. {
  190. case Option.Set:
  191. SshHandler.handler = handler;
  192. goto case Option.Optional;
  193. case Option.Optional:
  194. return true;
  195. }
  196. return false;
  197. }
  198. private static void SetUserPassword(Uri uri, out string user, out string password)
  199. {
  200. if (uri.UserInfo.Length != 0)
  201. {
  202. string[] userInfo = uri.UserInfo.Split(new char[] { ':' }, 2);
  203. user = userInfo[0];
  204. password = userInfo.Length == 2 ? userInfo[1] : null;
  205. }
  206. else
  207. {
  208. user = null;
  209. password = null;
  210. }
  211. }
  212. private static Handler FindHandler()
  213. {
  214. foreach (Handler handler in handlers)
  215. if (handler.Find())
  216. return handler;
  217. throw new Exception("Could not find a suitable SSH application.");
  218. }
  219. }