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 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. /*
  2. * Copyright (c) 2006-2015 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.events.StatusBarMessageEvent;
  24. import com.dmdirc.events.UnknownURLEvent;
  25. import com.dmdirc.interfaces.ConnectionManager;
  26. import com.dmdirc.interfaces.EventBus;
  27. import com.dmdirc.interfaces.config.AggregateConfigProvider;
  28. import com.dmdirc.ui.StatusMessage;
  29. import com.dmdirc.util.CommandUtils;
  30. import java.awt.Desktop;
  31. import java.io.IOException;
  32. import java.net.URI;
  33. import java.net.URISyntaxException;
  34. import java.net.URL;
  35. import java.util.Date;
  36. import javax.inject.Inject;
  37. import org.slf4j.Logger;
  38. import org.slf4j.LoggerFactory;
  39. import static com.dmdirc.util.LogUtils.USER_ERROR;
  40. /** Handles URLs. */
  41. public class URLHandler {
  42. private static final Logger LOG = LoggerFactory.getLogger(URLHandler.class);
  43. /** The time a browser was last launched. */
  44. private static Date lastLaunch;
  45. /** Event bus to fire unknown protocol errors on. */
  46. private final EventBus eventBus;
  47. /** Config manager. */
  48. private final AggregateConfigProvider config;
  49. /** Server manager to use to connect to servers. */
  50. private final ConnectionManager connectionManager;
  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 connectionManager Server manager to connect to servers
  59. */
  60. @Inject
  61. public URLHandler(
  62. final EventBus eventBus,
  63. final AggregateConfigProvider globalConfig,
  64. final ConnectionManager connectionManager) {
  65. this.eventBus = eventBus;
  66. this.config = globalConfig;
  67. this.connectionManager = connectionManager;
  68. this.desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
  69. }
  70. /**
  71. * Launches an application for a given url.
  72. *
  73. * @param urlString URL to parse
  74. */
  75. public void launchApp(final String urlString) {
  76. final String sanitisedString = getSanitisedString(urlString);
  77. URI uri;
  78. try {
  79. uri = new URI(sanitisedString);
  80. if (uri.getScheme() == null) {
  81. uri = new URI("http://" + sanitisedString);
  82. }
  83. } catch (URISyntaxException ex) {
  84. LOG.info(USER_ERROR, "Unavlid URL: {}", ex.getMessage(), ex);
  85. return;
  86. }
  87. launchApp(uri);
  88. }
  89. /**
  90. * Sanitises the specified string so that it may be used as a {@link URI}. Sanitisation consists
  91. * of:
  92. * <ul>
  93. * <li>replacing any pipe character with its hex escape</li>
  94. * <li>replacing any hash character in the fragment with its hex escape</li>
  95. * </ul>
  96. *
  97. * @since 0.6.5
  98. * @param urlString The string to be sanitised
  99. *
  100. * @return A sanitised version of the specified string.
  101. */
  102. protected static String getSanitisedString(final String urlString) {
  103. String sanitisedString = urlString.replace("|", "%7C");
  104. final int index = sanitisedString.indexOf('#');
  105. if (sanitisedString.lastIndexOf('#') > index) {
  106. sanitisedString = sanitisedString.substring(0, index + 1)
  107. + sanitisedString.substring(index + 1).replace("#", "%23");
  108. }
  109. return sanitisedString;
  110. }
  111. /**
  112. * Launches an application for a given url.
  113. *
  114. * @param url URL to parse
  115. */
  116. public void launchApp(final URL url) {
  117. URI uri;
  118. try {
  119. uri = url.toURI();
  120. if (uri.getScheme() == null) {
  121. uri = new URI("http://" + url);
  122. }
  123. } catch (URISyntaxException ex) {
  124. LOG.info(USER_ERROR, "Invalid URL: {}", ex.getMessage(), ex);
  125. return;
  126. }
  127. launchApp(uri);
  128. }
  129. /**
  130. * Launches an application for a given url.
  131. *
  132. * @param uri URL to parse
  133. */
  134. public void launchApp(final URI uri) {
  135. final Date oldLaunch = lastLaunch;
  136. lastLaunch = new Date();
  137. if (config.getOptionBool("browser", "uselaunchdelay") && oldLaunch != null) {
  138. final Long diff = lastLaunch.getTime() - oldLaunch.getTime();
  139. if (diff < config.getOptionInt("browser", "launchdelay")) {
  140. return;
  141. }
  142. }
  143. if (!config.hasOptionString("protocol", uri.getScheme().toLowerCase())) {
  144. eventBus.publish(new UnknownURLEvent(uri));
  145. return;
  146. }
  147. final String command = config.getOption("protocol", uri.getScheme().toLowerCase());
  148. switch (command) {
  149. case "DMDIRC":
  150. eventBus.publishAsync(new StatusBarMessageEvent(
  151. new StatusMessage("Connecting to: " + uri, config)));
  152. connectionManager.connectToAddress(uri);
  153. break;
  154. case "BROWSER":
  155. eventBus.publishAsync(new StatusBarMessageEvent(
  156. new StatusMessage("Opening: " + uri, config)));
  157. execBrowser(uri);
  158. break;
  159. case "MAIL":
  160. execMail(uri);
  161. break;
  162. default:
  163. eventBus.publishAsync(new StatusBarMessageEvent(
  164. new StatusMessage("Opening: " + uri, config)));
  165. execApp(substituteParams(uri, command));
  166. break;
  167. }
  168. }
  169. /**
  170. * Substitutes variables into a command.
  171. *
  172. * @param url data url
  173. * @param command command to be substituted
  174. *
  175. * @return Substituted command
  176. */
  177. public static String substituteParams(final URI url, final String command) {
  178. final String userInfo = url.getUserInfo();
  179. String fragment = "";
  180. String host = "";
  181. String path = "";
  182. String protocol = "";
  183. String query = "";
  184. String username = "";
  185. String password = "";
  186. String port = "";
  187. String newCommand = command;
  188. if (url.getFragment() != null) {
  189. fragment = url.getFragment();
  190. }
  191. if (url.getHost() != null) {
  192. host = url.getHost();
  193. }
  194. if (url.getPath() != null) {
  195. path = url.getPath();
  196. }
  197. if (url.getScheme() != null) {
  198. protocol = url.getScheme();
  199. }
  200. if (url.getQuery() != null) {
  201. query = url.getQuery();
  202. }
  203. if (url.getPort() > 0) {
  204. port = String.valueOf(url.getPort());
  205. }
  206. if (userInfo != null && !userInfo.isEmpty()) {
  207. if (userInfo.indexOf(':') == -1) {
  208. username = userInfo;
  209. } else {
  210. final int pos = userInfo.indexOf(':');
  211. username = userInfo.substring(0, pos);
  212. password = userInfo.substring(pos + 1);
  213. }
  214. }
  215. newCommand = newCommand.replace("$url", url.toString());
  216. newCommand = newCommand.replace("$fragment", fragment);
  217. newCommand = newCommand.replace("$host", host);
  218. newCommand = newCommand.replace("$path", path);
  219. newCommand = newCommand.replace("$port", port);
  220. newCommand = newCommand.replace("$query", query);
  221. newCommand = newCommand.replace("$protocol", protocol);
  222. newCommand = newCommand.replace("$username", username);
  223. newCommand = newCommand.replace("$password", password);
  224. return newCommand;
  225. }
  226. /**
  227. * Launches an application.
  228. *
  229. * @param command Application and arguments
  230. */
  231. private void execApp(final String command) {
  232. try {
  233. Runtime.getRuntime().exec(CommandUtils.parseArguments(command));
  234. } catch (IOException ex) {
  235. LOG.info(USER_ERROR, "Unable to run application: {}", ex.getMessage(), ex);
  236. }
  237. }
  238. /**
  239. * Opens the specified URL in the users browser.
  240. *
  241. * @param url URL to open
  242. */
  243. private void execBrowser(final URI url) {
  244. if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE)) {
  245. try {
  246. desktop.browse(url);
  247. } catch (IOException ex) {
  248. LOG.info(USER_ERROR, "Unable to open URL: {}", ex.getMessage(), ex);
  249. }
  250. } else {
  251. LOG.info(USER_ERROR, "Unable to open your browser: Your desktop enviroment is " +
  252. "not supported, please go to the URL Handlers section of " +
  253. "the preferences dialog and set the path to your browser manually");
  254. }
  255. }
  256. /**
  257. * Opens the specified URL in the users mail client.
  258. *
  259. * @param url URL to open
  260. */
  261. private void execMail(final URI url) {
  262. if (desktop != null && desktop.isSupported(Desktop.Action.MAIL)) {
  263. try {
  264. desktop.mail(url);
  265. } catch (IOException ex) {
  266. LOG.info(USER_ERROR, "Unable to open URL: {}", ex.getMessage(), ex);
  267. }
  268. } else {
  269. LOG.info(USER_ERROR, "Unable to open your mail client: Your desktop enviroment is " +
  270. "not supported, please go to the URL Handlers section of " +
  271. "the preferences dialog and set the path to your browser manually");
  272. }
  273. }
  274. }