You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

URLHandler.java 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. /*
  2. * Copyright (c) 2006-2014 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.ui.core.util;
  23. import com.dmdirc.ServerManager;
  24. import com.dmdirc.events.UnknownURLEvent;
  25. import com.dmdirc.events.UserErrorEvent;
  26. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  27. import com.dmdirc.logger.ErrorLevel;
  28. import com.dmdirc.ui.StatusMessage;
  29. import com.dmdirc.ui.core.components.StatusBarManager;
  30. import com.dmdirc.util.CommandUtils;
  31. import java.awt.Desktop;
  32. import java.io.IOException;
  33. import java.net.URI;
  34. import java.net.URISyntaxException;
  35. import java.net.URL;
  36. import java.util.Date;
  37. import javax.inject.Inject;
  38. import net.engio.mbassy.bus.MBassador;
  39. /** Handles URLs. */
  40. public class URLHandler {
  41. /** The time a browser was last launched. */
  42. private static Date lastLaunch;
  43. /** Event bus to fire unknown protocol errors on. */
  44. private final MBassador eventBus;
  45. /** Config manager. */
  46. private final AggregateConfigProvider config;
  47. /** Server manager to use to connect to servers. */
  48. private final ServerManager serverManager;
  49. /** Status bar manager to use to show messages. */
  50. private final StatusBarManager statusBarManager;
  51. /** Desktop handler. */
  52. private final Desktop desktop;
  53. /**
  54. * Instantiates a new URL Handler.
  55. *
  56. * @param eventBus Event bus to fire unknown protocol errors on.
  57. * @param globalConfig Config to retrieve settings from
  58. * @param serverManager Server manager to connect to servers
  59. * @param statusBarManager Status bar manager used to show messages
  60. */
  61. @Inject
  62. public URLHandler(
  63. final MBassador eventBus,
  64. final AggregateConfigProvider globalConfig,
  65. final ServerManager serverManager,
  66. final StatusBarManager statusBarManager) {
  67. this.eventBus = eventBus;
  68. this.config = globalConfig;
  69. this.serverManager = serverManager;
  70. this.statusBarManager = statusBarManager;
  71. this.desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
  72. }
  73. /**
  74. * Launches an application for a given url.
  75. *
  76. * @param urlString URL to parse
  77. */
  78. public void launchApp(final String urlString) {
  79. final String sanitisedString = getSanitisedString(urlString);
  80. URI uri;
  81. try {
  82. uri = new URI(sanitisedString);
  83. if (uri.getScheme() == null) {
  84. uri = new URI("http://" + sanitisedString);
  85. }
  86. } catch (URISyntaxException ex) {
  87. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, ex,
  88. "Invalid URL: " + ex.getMessage(), ""));
  89. return;
  90. }
  91. launchApp(uri);
  92. }
  93. /**
  94. * Sanitises the specified string so that it may be used as a {@link URI}. Sanitisation consists
  95. * of:
  96. * <ul>
  97. * <li>replacing any pipe character with its hex escape</li>
  98. * <li>replacing any hash character in the fragment with its hex escape</li>
  99. * </ul>
  100. *
  101. * @since 0.6.5
  102. * @param urlString The string to be sanitised
  103. *
  104. * @return A sanitised version of the specified string.
  105. */
  106. protected static String getSanitisedString(final String urlString) {
  107. String sanitisedString = urlString.replace("|", "%7C");
  108. final int index = sanitisedString.indexOf('#');
  109. if (sanitisedString.lastIndexOf('#') > index) {
  110. sanitisedString = sanitisedString.substring(0, index + 1)
  111. + sanitisedString.substring(index + 1).replace("#", "%23");
  112. }
  113. return sanitisedString;
  114. }
  115. /**
  116. * Launches an application for a given url.
  117. *
  118. * @param url URL to parse
  119. */
  120. public void launchApp(final URL url) {
  121. URI uri;
  122. try {
  123. uri = url.toURI();
  124. if (uri.getScheme() == null) {
  125. uri = new URI("http://" + url.toString());
  126. }
  127. } catch (URISyntaxException ex) {
  128. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, ex,
  129. "Invalid URL: " + ex.getMessage(), ""));
  130. return;
  131. }
  132. launchApp(uri);
  133. }
  134. /**
  135. * Launches an application for a given url.
  136. *
  137. * @param uri URL to parse
  138. */
  139. public void launchApp(final URI uri) {
  140. final Date oldLaunch = lastLaunch;
  141. lastLaunch = new Date();
  142. if (config.getOptionBool("browser", "uselaunchdelay") && oldLaunch != null) {
  143. final Long diff = lastLaunch.getTime() - oldLaunch.getTime();
  144. if (diff < config.getOptionInt("browser", "launchdelay")) {
  145. return;
  146. }
  147. }
  148. if (!config.hasOptionString("protocol", uri.getScheme().toLowerCase())) {
  149. eventBus.publish(new UnknownURLEvent(uri));
  150. return;
  151. }
  152. final String command = config.getOption("protocol", uri.getScheme().toLowerCase());
  153. switch (command) {
  154. case "DMDIRC":
  155. statusBarManager.setMessage(
  156. new StatusMessage("Connecting to: " + uri.toString(),
  157. config));
  158. serverManager.connectToAddress(uri);
  159. break;
  160. case "BROWSER":
  161. statusBarManager.setMessage(
  162. new StatusMessage("Opening: " + uri.toString(),
  163. config));
  164. execBrowser(uri);
  165. break;
  166. case "MAIL":
  167. execMail(uri);
  168. break;
  169. default:
  170. statusBarManager.setMessage(
  171. new StatusMessage("Opening: " + uri.toString(),
  172. config));
  173. execApp(substituteParams(uri, command));
  174. break;
  175. }
  176. }
  177. /**
  178. * Substitutes variables into a command.
  179. *
  180. * @param url data url
  181. * @param command command to be substituted
  182. *
  183. * @return Substituted command
  184. */
  185. public static String substituteParams(final URI url, final String command) {
  186. final String userInfo = url.getUserInfo();
  187. String fragment = "";
  188. String host = "";
  189. String path = "";
  190. String protocol = "";
  191. String query = "";
  192. String username = "";
  193. String password = "";
  194. String port = "";
  195. String newCommand = command;
  196. if (url.getFragment() != null) {
  197. fragment = url.getFragment();
  198. }
  199. if (url.getHost() != null) {
  200. host = url.getHost();
  201. }
  202. if (url.getPath() != null) {
  203. path = url.getPath();
  204. }
  205. if (url.getScheme() != null) {
  206. protocol = url.getScheme();
  207. }
  208. if (url.getQuery() != null) {
  209. query = url.getQuery();
  210. }
  211. if (url.getPort() > 0) {
  212. port = String.valueOf(url.getPort());
  213. }
  214. if (userInfo != null && !userInfo.isEmpty()) {
  215. if (userInfo.indexOf(':') == -1) {
  216. username = userInfo;
  217. } else {
  218. final int pos = userInfo.indexOf(':');
  219. username = userInfo.substring(0, pos);
  220. password = userInfo.substring(pos + 1);
  221. }
  222. }
  223. newCommand = newCommand.replace("$url", url.toString());
  224. newCommand = newCommand.replace("$fragment", fragment);
  225. newCommand = newCommand.replace("$host", host);
  226. newCommand = newCommand.replace("$path", path);
  227. newCommand = newCommand.replace("$port", port);
  228. newCommand = newCommand.replace("$query", query);
  229. newCommand = newCommand.replace("$protocol", protocol);
  230. newCommand = newCommand.replace("$username", username);
  231. newCommand = newCommand.replace("$password", password);
  232. return newCommand;
  233. }
  234. /**
  235. * Launches an application.
  236. *
  237. * @param command Application and arguments
  238. */
  239. private void execApp(final String command) {
  240. try {
  241. Runtime.getRuntime().exec(CommandUtils.parseArguments(command));
  242. } catch (IOException ex) {
  243. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, ex,
  244. "Unable to run application: " + ex.getMessage(), ""));
  245. }
  246. }
  247. /**
  248. * Opens the specified URL in the users browser.
  249. *
  250. * @param url URL to open
  251. */
  252. private void execBrowser(final URI url) {
  253. if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
  254. try {
  255. desktop.browse(url);
  256. } catch (IOException ex) {
  257. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, ex,
  258. "Unable to open URL: " + ex.getMessage(), ""));
  259. }
  260. } else {
  261. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, null,
  262. "Unable to open your browser: Your desktop enviroment is "
  263. + "not supported, please go to the URL Handlers section of "
  264. + "the preferences dialog and set the path to your browser "
  265. + "manually", ""));
  266. }
  267. }
  268. /**
  269. * Opens the specified URL in the users mail client.
  270. *
  271. * @param url URL to open
  272. */
  273. private void execMail(final URI url) {
  274. if (desktop != null && desktop.isSupported(Desktop.Action.MAIL)) {
  275. try {
  276. desktop.mail(url);
  277. } catch (IOException ex) {
  278. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, ex,
  279. "Unable to open URL: " + ex.getMessage(), ""));
  280. }
  281. } else {
  282. eventBus.publish(new UserErrorEvent(ErrorLevel.LOW, null,
  283. "Unable to open your mail client: Your desktop enviroment is "
  284. + "not supported, please go to the URL Handlers section of "
  285. + "the preferences dialog and set the path to your browser "
  286. + "manually", ""));
  287. }
  288. }
  289. }