From ec85372132fae1d52f54a78f392a861fd30b630e Mon Sep 17 00:00:00 2001 From: Pavel Djundik Date: Sun, 24 Nov 2019 00:45:04 +0200 Subject: [PATCH] Fix uri handling and add tests --- client/components/Windows/Connect.vue | 13 ++- client/js/helpers/parseIrcUri.js | 65 ++++++++++++++ client/js/socket-events/init.js | 58 +----------- test/client/js/helpers/parseIrcUri.js | 121 ++++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 56 deletions(-) create mode 100644 client/js/helpers/parseIrcUri.js create mode 100644 test/client/js/helpers/parseIrcUri.js diff --git a/client/components/Windows/Connect.vue b/client/components/Windows/Connect.vue index 46120931..f9f6d156 100644 --- a/client/components/Windows/Connect.vue +++ b/client/components/Windows/Connect.vue @@ -39,8 +39,12 @@ export default { continue; } + let value = params[key]; + // Param can contain multiple values in an array if its supplied more than once - let value = typeof params[key] === "string" ? params[key] : params[key][0]; + if (Array.isArray(value)) { + value = value[0]; + } // Support `channels` as a compatibility alias with other clients if (key === "channels") { @@ -85,7 +89,12 @@ export default { // Override server provided defaults with parameters passed in the URL if they match the data type switch (typeof this.$store.state.serverConfiguration.defaults[key]) { case "boolean": - parsedParams[key] = value === "1" || value === "true"; + if (value === "0" || value === "false") { + parsedParams[key] = false; + } else { + parsedParams[key] = !!value; + } + break; case "number": parsedParams[key] = Number(value); diff --git a/client/js/helpers/parseIrcUri.js b/client/js/helpers/parseIrcUri.js new file mode 100644 index 00000000..8bb3b64b --- /dev/null +++ b/client/js/helpers/parseIrcUri.js @@ -0,0 +1,65 @@ +"use strict"; + +export default (stringUri) => { + const data = {}; + + try { + // https://tools.ietf.org/html/draft-butcher-irc-url-04 + const uri = new URL(stringUri); + + // Replace protocol with a "special protocol" (that's what it's called in WHATWG spec) + // So that the uri can be properly parsed + if (uri.protocol === "irc:") { + uri.protocol = "http:"; + + if (!uri.port) { + uri.port = 6667; + } + + data.tls = false; + } else if (uri.protocol === "ircs:") { + uri.protocol = "https:"; + + if (!uri.port) { + uri.port = 6697; + } + + data.tls = true; + } else { + return; + } + + if (!uri.hostname) { + return {}; + } + + data.host = data.name = uri.hostname; + data.port = uri.port; + data.username = decodeURIComponent(uri.username); + data.password = decodeURIComponent(uri.password); + + let channel = ""; + + if (uri.pathname.length > 1) { + channel = uri.pathname; + } else if (uri.hash.length > 1) { + channel = uri.hash; + } + + if (channel) { + channel = channel.substr(1); // remove / or # + + const index = channel.indexOf(","); + + if (index > -1) { + channel = channel.substring(0, index); + } + } + + data.join = channel; + } catch (e) { + // do nothing on invalid uri + } + + return data; +}; diff --git a/client/js/socket-events/init.js b/client/js/socket-events/init.js index 4cec3e80..2638867b 100644 --- a/client/js/socket-events/init.js +++ b/client/js/socket-events/init.js @@ -4,6 +4,7 @@ import socket from "../socket"; import storage from "../localStorage"; import {router, switchToChannel, navigate} from "../router"; import store from "../store"; +import parseIrcUri from "../helpers/parseIrcUri"; socket.on("init", function(data) { store.commit("networks", mergeNetworkData(data.networks)); @@ -157,67 +158,16 @@ function handleQueryParams() { if (params.has("uri")) { // Set default connection settings from IRC protocol links - const uri = - params.get("uri") + - (location.hash.startsWith("#/") ? `#${location.hash.substring(2)}` : location.hash); + const uri = params.get("uri"); const queryParams = parseIrcUri(uri); cleanParams(); - router.router.push({name: "Connect", query: queryParams}); + router.push({name: "Connect", query: queryParams}); } else if (document.body.classList.contains("public") && document.location.search) { // Set default connection settings from url params const queryParams = Object.fromEntries(params.entries()); cleanParams(); - router.router.push({name: "Connect", query: queryParams}); + router.push({name: "Connect", query: queryParams}); } } - -function parseIrcUri(stringUri) { - const data = {}; - - try { - // https://tools.ietf.org/html/draft-butcher-irc-url-04 - const uri = new URL(stringUri); - - // Replace protocol with a "special protocol" (that's what it's called in WHATWG spec) - // So that the uri can be properly parsed - if (uri.protocol === "irc:") { - uri.protocol = "http:"; - - if (!uri.port) { - uri.port = 6667; - } - - data.tls = false; - } else if (uri.protocol === "ircs:") { - uri.protocol = "https:"; - - if (!uri.port) { - uri.port = 6697; - } - - data.tls = true; - } else { - return; - } - - data.host = data.name = uri.hostname; - data.port = uri.port; - data.username = window.decodeURIComponent(uri.username); - data.password = window.decodeURIComponent(uri.password); - - let channel = (uri.pathname + uri.hash).substr(1); - const index = channel.indexOf(","); - - if (index > -1) { - channel = channel.substring(0, index); - } - - data.join = channel; - } catch (e) { - // do nothing on invalid uri - } - - return data; -} diff --git a/test/client/js/helpers/parseIrcUri.js b/test/client/js/helpers/parseIrcUri.js new file mode 100644 index 00000000..e69c3012 --- /dev/null +++ b/test/client/js/helpers/parseIrcUri.js @@ -0,0 +1,121 @@ +"use strict"; + +const expect = require("chai").expect; +const parseIrcUri = require("../../../../client/js/helpers/parseIrcUri").default; + +describe("parseIrcUri helper", function() { + it("should parse irc:// without port", function() { + expect(parseIrcUri("irc://example.com")).to.deep.equal({ + tls: false, + name: "example.com", + host: "example.com", + port: "6667", + username: "", + password: "", + join: "", + }); + }); + + it("should parse ircs:// without port", function() { + expect(parseIrcUri("ircs://example.com")).to.deep.equal({ + tls: true, + name: "example.com", + host: "example.com", + port: "6697", + username: "", + password: "", + join: "", + }); + }); + + it("should parse irc:// with port", function() { + expect(parseIrcUri("irc://example.com:1337")).to.deep.equal({ + tls: false, + name: "example.com", + host: "example.com", + port: "1337", + username: "", + password: "", + join: "", + }); + }); + + it("should parse ircs:// with port", function() { + expect(parseIrcUri("ircs://example.com:1337")).to.deep.equal({ + tls: true, + name: "example.com", + host: "example.com", + port: "1337", + username: "", + password: "", + join: "", + }); + }); + + it("should parse username, password and port", function() { + expect(parseIrcUri("ircs://user:password@example.com:1337")).to.deep.equal({ + tls: true, + name: "example.com", + host: "example.com", + port: "1337", + username: "user", + password: "password", + join: "", + }); + }); + + it("should parse channel from query", function() { + expect(parseIrcUri("ircs://example.com:1337/channel,channel2")).to.deep.equal({ + tls: true, + name: "example.com", + host: "example.com", + port: "1337", + username: "", + password: "", + join: "channel", + }); + }); + + it("should parse channel from hash", function() { + const obj = { + tls: true, + name: "example.com", + host: "example.com", + port: "1337", + username: "", + password: "", + join: "channel", + }; + + expect(parseIrcUri("ircs://example.com:1337#channel,channel2")).to.deep.equal(obj); + expect(parseIrcUri("ircs://example.com:1337/#channel,channel2")).to.deep.equal(obj); + }); + + it("accepts query over hash", function() { + expect(parseIrcUri("ircs://example.com:1337/channel#channel2")).to.deep.equal({ + tls: true, + name: "example.com", + host: "example.com", + port: "1337", + username: "", + password: "", + join: "channel", + }); + }); + + it("should not parse invalid port", function() { + expect(parseIrcUri("ircs://example.com:lol")).to.deep.equal({}); + }); + + it("should not channel on empty query and hash", function() { + expect(parseIrcUri("irc://example.com/#")).to.deep.equal({ + tls: false, + name: "example.com", + host: "example.com", + port: "6667", + username: "", + password: "", + join: "", + }); + }); +});