some improvements
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
Jonathan 2020-10-23 18:04:02 +02:00
parent 51905ab3f2
commit acb5e0104a
4 changed files with 98 additions and 21 deletions

View file

@ -1,36 +1,67 @@
const {q, ElemJS, ejs} = require("./basic.js") const {q, ElemJS, ejs} = require("./basic.js")
const {S_RE_DOMAIN, S_RE_IPV6, S_RE_IPV4} = require("./re.js")
const password = q("#password") const password = q("#password")
const homeserver = q("#homeserver")
// A regex matching a lossy MXID
// Groups:
// 1: username/localpart
// MAYBE WITH
// 2: hostname/serverpart
// 3: domain
// OR
// 4: IP address
// 5: IPv4 address
// OR
// 6: IPv6 address
// MAYBE WITH
// 7: port number
const RE_LOSSY_MXID = new RegExp(`^@?([a-z0-9._=-]+?)(?::((?:(${S_RE_DOMAIN})|((${S_RE_IPV4})|(${S_RE_IPV6})))(?::(\\d+))?))?$`)
window.RE_LOSSY_MXID = RE_LOSSY_MXID
class Username extends ElemJS { class Username extends ElemJS {
constructor() { constructor(homeserver) {
super(q("#username")) super(q("#username"))
this.homeserver = homeserver;
this.on("change", this.updateServer.bind(this)) this.on("change", this.updateServer.bind(this))
} }
isValid() { isValid() {
return !!this.element.value.match(/^@?[a-z0-9._=\/-]+(?::[a-zA-Z0-9.:\[\]-]+)?$/) return !!this.element.value.match(RE_LOSSY_MXID)
} }
getUsername() { getUsername() {
return this.element.value.match(/^@?([a-z0-9._=\/-]+)/)[1] return this.element.value.match(RE_LOSSY_MXID)[1]
} }
getServer() { getServer() {
const server = this.element.value.match(/^@?[a-z0-9._=\?-]+:([a-zA-Z0-9.:\[\]-]+)$/) const server = this.element.value.match(RE_LOSSY_MXID)
if (server && server[1]) return server[1] if (server && server[2]) return server[2]
else return null else return null
} }
updateServer() { updateServer() {
if (!this.isValid()) return if (!this.isValid()) return
if (this.getServer()) homeserver.value = this.getServer() if (this.getServer()) this.homeserver.suggest(this.getServer())
} }
} }
const username = new Username() class Homeserver extends ElemJS {
constructor() {
super(q("#homeserver"));
}
suggest(value) {
this.element.placeholder = value
}
getServer() {
return this.element.value || this.element.placeholder;
}
}
class Feedback extends ElemJS { class Feedback extends ElemJS {
constructor() { constructor() {
@ -60,13 +91,14 @@ class Feedback extends ElemJS {
} }
} }
const feedback = new Feedback()
class Form extends ElemJS { class Form extends ElemJS {
constructor() { constructor(username, feedback, homeserver) {
super(q("#form")) super(q("#form"))
this.processing = false this.processing = false
this.username = username
this.feedback = feedback
this.homeserver = homeserver
this.on("submit", this.submit.bind(this)) this.on("submit", this.submit.bind(this))
} }
@ -74,12 +106,12 @@ class Form extends ElemJS {
async submit() { async submit() {
if (this.processing) return if (this.processing) return
this.processing = true this.processing = true
if (!username.isValid()) return this.cancel("Username is not valid.") if (!this.username.isValid()) return this.cancel("Username is not valid.")
// Resolve homeserver address // Resolve homeserver address
let domain let domain
try { try {
domain = await this.findHomeserver(homeserver.value) domain = await this.findHomeserver(this.homeserver.getServer())
} catch(e) { } catch(e) {
return this.cancel(e.message) return this.cancel(e.message)
} }
@ -90,7 +122,7 @@ class Form extends ElemJS {
method: "POST", method: "POST",
body: JSON.stringify({ body: JSON.stringify({
type: "m.login.password", type: "m.login.password",
user: username.getUsername(), user: this.username.getUsername(),
password: password.value password: password.value
}) })
}).then(res => res.json()) }).then(res => res.json())
@ -153,15 +185,21 @@ class Form extends ElemJS {
} }
status(message) { status(message) {
feedback.setLoading(true) this.feedback.setLoading(true)
feedback.message(message) this.feedback.message(message)
} }
cancel(message) { cancel(message) {
this.processing = false this.processing = false
feedback.setLoading(false) this.feedback.setLoading(false)
feedback.message(message, true) this.feedback.message(message, true)
} }
} }
const form = new Form() const homeserver = new Homeserver()
const username = new Username(homeserver)
const feedback = new Feedback()
const form = new Form(username, feedback, homeserver)

38
src/js/re.js Normal file
View file

@ -0,0 +1,38 @@
// A valid internet domain, according to https://stackoverflow.com/a/20046959 (cleaned)
const S_RE_DOMAIN = "(?:[a-zA-Z]|[a-zA-Z][a-zA-Z]|[a-zA-Z]\\d|\\d[a-zA-Z]|[a-zA-Z0-9][a-zA-Z0-9-_]{1,61}[a-zA-Z0-9])\\.(?:[a-zA-Z]{2,6}|[a-zA-Z0-9-]{2,30}\\.[a-zA-Z]{2,3})"
// A valid ipv4 address, one that doesn't check for valid numbers (e.g. not 999) and one that does
// const S_RE_IPV4_NO_CHECK = "(?:(?:\\d{1,3}\\.){3}\\d{1,3})"
const S_RE_IPV4_HAS_CHECK = "(?:(?:25[0-5]|(?:2[0-4]|1{0,1}\\d){0,1}\\d)\\.){3}(?:25[0-5]|(?:2[0-4]|1{0,1}\\d){0,1}\\d)"
const S_RE_IPV6_SEG = "[a-fA-F\\d]{1,4}"
// Yes, this is an ipv6 address.
const S_RE_IPV6 = `
(?:
(?:${S_RE_IPV6_SEG}:){7}(?:${S_RE_IPV6_SEG}|:)|
(?:${S_RE_IPV6_SEG}:){6}(?:${S_RE_IPV4_HAS_CHECK}|:${S_RE_IPV6_SEG}|:)|
(?:${S_RE_IPV6_SEG}:){5}(?::${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,2}|:)|
(?:${S_RE_IPV6_SEG}:){4}(?:(?::${S_RE_IPV6_SEG}){0,1}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,3}|:)|
(?:${S_RE_IPV6_SEG}:){3}(?:(?::${S_RE_IPV6_SEG}){0,2}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,4}|:)|
(?:${S_RE_IPV6_SEG}:){2}(?:(?::${S_RE_IPV6_SEG}){0,3}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,5}|:)|
(?:${S_RE_IPV6_SEG}:){1}(?:(?::${S_RE_IPV6_SEG}){0,4}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,6}|:)|
(?::(?:(?::${S_RE_IPV6_SEG}){0,5}:${S_RE_IPV4_HAS_CHECK}|(?::${S_RE_IPV6_SEG}){1,7}|:))
)(?:%[0-9a-zA-Z]{1,})?`
.replace(/\s*\/\/.*$/gm, '')
.replace(/\n/g, '')
.trim();
const RE_DOMAIN_EXACT = new RegExp(`^${S_RE_DOMAIN}$`)
const RE_IPV4_EXACT = new RegExp(`^${S_RE_IPV4_HAS_CHECK}$`)
const RE_IPV6_EXACT = new RegExp(`^${S_RE_IPV6}$`)
const RE_IP_ADDR_EXACT = new RegExp(`^${S_RE_IPV6}|${S_RE_IPV4_HAS_CHECK}$`)
module.exports = {
S_RE_DOMAIN,
S_RE_IPV6,
S_RE_IPV4: S_RE_IPV4_HAS_CHECK,
RE_DOMAIN_EXACT,
RE_IPV4_EXACT,
RE_IPV6_EXACT,
RE_IP_ADDR_EXACT
}

View file

@ -15,7 +15,7 @@ html
.data-input .data-input
.form-input-container .form-input-container
label(for="username") Username label(for="username") Username
input(type="text" name="username" autocomplete="username" placeholder="@username:server.tld" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username input(type="text" name="username" autocomplete="username" placeholder="@username:server.com" pattern="^@?[a-z0-9._=/-]+(?::[a-zA-Z0-9.:\\[\\]-]+)?$" required)#username
.form-input-container .form-input-container
label(for="password") Password label(for="password") Password
@ -23,7 +23,7 @@ html
.form-input-container .form-input-container
label(for="homeserver") Homeserver label(for="homeserver") Homeserver
input(type="text" name="homeserver" value="matrix.org" placeholder="matrix.org" required)#homeserver input(type="text" name="homeserver" placeholder="matrix.org")#homeserver
#feedback #feedback

View file

@ -78,6 +78,7 @@ button, input[type="submit"]
padding: 7px padding: 7px
&:hover &:hover
cursor: pointer
background-color: c.$milder background-color: c.$milder
label label