Everyone signs in at the one central Quine identity server and is called by username. The caller picks which Jitsi Meet server each call happens on (meet.jit.si by default, or any other deployment), and the invite sends the callee to the same one. Jitsi servers need no credentials from Quple. - server: POST /call takes an optional jitsiUrl (http(s) only, no credentials/query/fragment; default DEFAULT_JITSI) and returns it with the meeting ID; invites carry it. Adds GET /me and per-person contacts with presence (persisted in DATA_DIR), replacing GET /users. - desktop: "On server" field for calls, remembered as the last-used server; incoming calls show which server they're on; open_meeting refuses non-http(s) URLs. meet.jit.si sign-in is now an inline prompt when calling there rather than a gate in front of the whole app. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
38 lines
1.4 KiB
Go
38 lines
1.4 KiB
Go
package main
|
|
|
|
import (
|
|
"errors"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
)
|
|
|
|
// usernamePattern matches the identity server's: usernames are what people
|
|
// type to call each other, and they end up in URLs and meeting display names.
|
|
var usernamePattern = regexp.MustCompile(`^[a-z0-9][a-z0-9._-]{0,31}$`)
|
|
|
|
// normalizeUsername lowercases s and checks it's a well-formed Quine username.
|
|
func normalizeUsername(s string) (string, error) {
|
|
s = strings.ToLower(strings.TrimSpace(s))
|
|
if !usernamePattern.MatchString(s) {
|
|
return "", errors.New("a username is 1-32 chars of a-z, 0-9, '.', '_' or '-', starting with a letter or digit")
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
// normalizeJitsiURL checks s is the base URL of a Jitsi Meet deployment, like
|
|
// https://meet.jit.si, and returns it without a trailing slash. Any server
|
|
// will do - the callee's app opens it, so it only has to be a plain http(s)
|
|
// URL that can't smuggle anything else along.
|
|
func normalizeJitsiURL(s string) (string, error) {
|
|
s = strings.TrimRight(strings.TrimSpace(s), "/")
|
|
if !strings.Contains(s, "://") {
|
|
s = "https://" + s
|
|
}
|
|
u, err := url.Parse(s)
|
|
if err != nil || (u.Scheme != "https" && u.Scheme != "http") || u.Hostname() == "" ||
|
|
u.User != nil || u.RawQuery != "" || u.Fragment != "" || len(s) > 200 {
|
|
return "", errors.New("the server must be a Jitsi Meet URL, like https://meet.jit.si")
|
|
}
|
|
return u.Scheme + "://" + strings.ToLower(u.Host) + u.EscapedPath(), nil
|
|
}
|