Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package galene for openSUSE:Factory checked in at 2021-11-05 22:59:03 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/galene (Old) and /work/SRC/openSUSE:Factory/.galene.new.1890 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "galene" Fri Nov 5 22:59:03 2021 rev:12 rq:929717 version:0.4.1 Changes: -------- --- /work/SRC/openSUSE:Factory/galene/galene.changes 2021-09-06 15:58:07.825293815 +0200 +++ /work/SRC/openSUSE:Factory/.galene.new.1890/galene.changes 2021-11-05 22:59:19.632308768 +0100 @@ -1,0 +2,23 @@ +Fri Nov 5 16:50:04 UTC 2021 - Michael Str??der <mich...@stroeder.com> + +- adapted AppArmor profile to new config files in 0.4.1 + +------------------------------------------------------------------- +Fri Nov 05 14:43:31 UTC 2021 - mich...@stroeder.com + +- Update to version 0.4.1: + * Create a new file data/config.json with global configuration. + * Remove data/passwd and the -redirect option with entries in config.json; + these are incompatible changes. + * Change the group URL from /group/name to /group/name/, which allows + using relative links. The old URL is redirected to the new one. + * Extend the protocol with the ability to consult the group status before + joining; this allows using the group's displayName before the user has + logged in, and will be required for new authorisation schemes. + * Allow scrolling of the login dialog, useful on small devices. + * Fixed a typo that prevented the group name from being displayed. + * Made failed videos more visible. + * No longer attempt to save passwords in browser JavaScript, it's + insecure and not very useful. + +------------------------------------------------------------------- Old: ---- galene-0.4.tar.gz New: ---- galene-0.4.1.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ galene.spec ++++++ --- /var/tmp/diff_new_pack.ZryNPI/_old 2021-11-05 22:59:20.164309123 +0100 +++ /var/tmp/diff_new_pack.ZryNPI/_new 2021-11-05 22:59:20.168309125 +0100 @@ -25,7 +25,7 @@ %bcond_without apparmor Name: galene -Version: 0.4 +Version: 0.4.1 Release: 0 Summary: Gal??ne videoconferencing server License: MIT ++++++ _service ++++++ --- /var/tmp/diff_new_pack.ZryNPI/_old 2021-11-05 22:59:20.196309145 +0100 +++ /var/tmp/diff_new_pack.ZryNPI/_new 2021-11-05 22:59:20.196309145 +0100 @@ -3,8 +3,8 @@ <param name="url">git://github.com/jech/galene.git</param> <param name="scm">git</param> <param name="exclude">.git</param> - <param name="revision">galene-0.4</param> - <param name="version">0.4</param> + <param name="revision">galene-0.4.1</param> + <param name="version">0.4.1</param> <param name="changesgenerate">enable</param> <!--param name="versionrewrite-pattern">galene-(\d+)</param> <param name="versionrewrite-replacement">\1</param--> ++++++ _servicedata ++++++ --- /var/tmp/diff_new_pack.ZryNPI/_old 2021-11-05 22:59:20.216309158 +0100 +++ /var/tmp/diff_new_pack.ZryNPI/_new 2021-11-05 22:59:20.216309158 +0100 @@ -1,4 +1,4 @@ <servicedata> <service name="tar_scm"> <param name="url">git://github.com/jech/galene.git</param> - <param name="changesrevision">d33e4dea9ba74818d35ebde8f550b6a0ab0f75ad</param></service></servicedata> \ No newline at end of file + <param name="changesrevision">37d6ab5445f5f0279e17694dd774fd86610e6c5e</param></service></servicedata> \ No newline at end of file ++++++ apparmor-usr.sbin.galene ++++++ --- /var/tmp/diff_new_pack.ZryNPI/_old 2021-11-05 22:59:20.232309168 +0100 +++ /var/tmp/diff_new_pack.ZryNPI/_new 2021-11-05 22:59:20.232309168 +0100 @@ -22,7 +22,7 @@ /etc/galene/cert.pem r, /etc/galene/key.pem r, /etc/galene/ice-servers.json r, - /etc/galene/passwd r, + /etc/galene/config.json r, # Grant read access to static web content /usr/share/galene/static/ r, ++++++ galene-0.4.tar.gz -> galene-0.4.1.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/CHANGES new/galene-0.4.1/CHANGES --- old/galene-0.4/CHANGES 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/CHANGES 2021-11-05 15:31:09.000000000 +0100 @@ -1,3 +1,19 @@ +4 November 2021: Gal??ne 0.4.1 + + * Create a new file data/config.json with global configuration. + * Remove data/passwd and the -redirect option with entries in config.json; + these are incompatible changes. + * Change the group URL from /group/name to /group/name/, which allows + using relative links. The old URL is redirected to the new one. + * Extend the protocol with the ability to consult the group status before + joining; this allows using the group's displayName before the user has + logged in, and will be required for new authorisation schemes. + * Allow scrolling of the login dialog, useful on small devices. + * Fixed a typo that prevented the group name from being displayed. + * Made failed videos more visible. + * No longer attempt to save passwords in browser JavaScript, it's + insecure and not very useful. + 5 September 2021: Gal??ne 0.4 * Implemented simulcast. diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/INSTALL new/galene-0.4.1/INSTALL --- old/galene-0.4/INSTALL 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/INSTALL 2021-11-05 15:31:09.000000000 +0100 @@ -12,15 +12,6 @@ go build -ldflags="-s -w" -## Set the server administrator credentials - -This step is optional, it is currently only relevant for access to the -`/stats.html` and `/stats.json` locations. - - mkdir data - echo 'god:topsecret' > data/passwd - - ## Set up a group Set up a group called *test* by creating a file `groups/test.json`: diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/README new/galene-0.4.1/README --- old/galene-0.4/README 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/README 2021-11-05 15:31:09.000000000 +0100 @@ -37,6 +37,23 @@ not. +# The global configuration file + +The server may be configured in the JSON file `data/config.json`. This +file may look as follows: + + { + "canonicalHost: "galene.example.org", + "admin":[{"username":"root","password":"secret"}] + } + +The fields are as follows: + +- `canonicalHost`: the canonical name of the host running the server; +- `admin` defines the users allowed to look at the `/stats.html` file; it + has the same syntax as user definitions in groups (see below). + + # Group definitions Groups are defined by files in the `./groups` directory (this may be diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/README.PROTOCOL new/galene-0.4.1/README.PROTOCOL --- old/galene-0.4/README.PROTOCOL 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/README.PROTOCOL 2021-11-05 15:31:09.000000000 +0100 @@ -1,31 +1,12 @@ # Gal??ne's protocol -Gal??ne uses a symmetric, asynchronous protocol. In client-server -usage, some messages are only sent in the client to server or in the -server to client direction. - -## Message syntax - -All messages are sent as JSON objects. All fields except `type` are -optional; however, there are some fields that are common across multiple -message types. - - - `type`, the type of the message; - - `kind`, the subtype of the message; - - `id`, the id of the object being manipulated; - - `source`, the client-id of the originating client; - - `username`, the username of the originating client; - - `dest`, the client-id of the destination client; - - `privileged`, set by the server to indicate that the originating client - had the `op` privilege at the time it sent the message. - ## Data structures ### Group A group is a set of clients. It is identified by a human-readable name -that must not start or end with a slash "`/`" and must not have the -substrings "`/../`" or "`/./`". +that must not start or end with a slash "`/`", must not start with +a period "`.`", and must not contain the substrings "`/../`" or "`/./`". ### Client @@ -41,6 +22,48 @@ allowed). The offerer is also the RTP sender (i.e. all tracks sent by the offerer are of type `sendonly`). +Gal??ne uses a symmetric, asynchronous protocol. In client-server +usage, some messages are only sent in the client to server or in the +server to client direction. + +## Before connecting + +Before it connects and joins a group, a client may perform an HTTP GET +request on the URL `/public-groups.json`. This yields a JSON array of +objects, one for each group that has been marked public in its +configuration file. Each object has the following fields: + + - `name`: the group's name + - `displayName` (optional): a longer version of the name used for display; + - `description` (optional): a user-readable description. + - `locked`: true if the group is locked; + - `clientCount`: the number of clients currently in the group. + +A client may also fetch the URL `/group/name/.status.json` to retrieve the +status of a single group. If the group has not been marked as public, +then the fields `locked` and `clientCount` are omitted. + +## Connecting + +The client connects to the websocket at `/ws`. Galene uses a symmetric, +asynchronous protocol: there are no requests and responses, and most +messages may be sent by either peer. + +## Message syntax + +All messages are sent as JSON objects. All fields except `type` are +optional; however, there are some fields that are common across multiple +message types: + + - `type`, the type of the message; + - `kind`, the subtype of the message; + - `id`, the id of the object being manipulated; + - `source`, the client-id of the originating client; + - `username`, the username of the originating client; + - `dest`, the client-id of the destination client; + - `privileged`, set by the server to indicate that the originating client + had the `op` privilege at the time when it sent the message. + ## Establishing and maintaining a connection The peer establishing the connection (the WebSocket client) sends @@ -107,8 +130,8 @@ The `permissions` field is an array of strings that may contain the values `present`, `op` and `record`. The `status` field is a dictionary that -contains random information that can be usefully displayed in the user -interface. +contains status information about the group, in the same format as at the +`.status.json` URL above. ## Maintaining group membership diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/diskwriter/diskwriter.go new/galene-0.4.1/diskwriter/diskwriter.go --- old/galene-0.4/diskwriter/diskwriter.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/diskwriter/diskwriter.go 2021-11-05 15:31:09.000000000 +0100 @@ -59,10 +59,6 @@ return "RECORDING" } -func (client *Client) Challenge(group string, cred group.ClientCredentials) bool { - return true -} - func (client *Client) SetPermissions(perms group.ClientPermissions) { return } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/galene-password-generator/galene-password-generator.go new/galene-0.4.1/galene-password-generator/galene-password-generator.go --- old/galene-0.4/galene-password-generator/galene-password-generator.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/galene-password-generator/galene-password-generator.go 2021-11-05 15:31:09.000000000 +0100 @@ -56,7 +56,7 @@ } e := json.NewEncoder(os.Stdout) if username != "" { - creds := group.ClientCredentials{ + creds := group.ClientPattern{ Username: username, Password: &p, } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/galene.go new/galene-0.4.1/galene.go --- old/galene-0.4/galene.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/galene.go 2021-11-05 15:31:09.000000000 +0100 @@ -21,17 +21,15 @@ ) func main() { - var cpuprofile, memprofile, mutexprofile, httpAddr, dataDir string + var cpuprofile, memprofile, mutexprofile, httpAddr string var udpRange string flag.StringVar(&httpAddr, "http", ":8443", "web server `address`") flag.StringVar(&webserver.StaticRoot, "static", "./static/", "web server root `directory`") - flag.StringVar(&webserver.Redirect, "redirect", "", - "redirect to canonical `host`") flag.BoolVar(&webserver.Insecure, "insecure", false, "act as an HTTP server rather than HTTPS") - flag.StringVar(&dataDir, "data", "./data/", + flag.StringVar(&group.DataDirectory, "data", "./data/", "data `directory`") flag.StringVar(&group.Directory, "groups", "./groups/", "group description `directory`") @@ -112,7 +110,7 @@ log.Printf("File descriptor limit is %v, please increase it!", n) } - ice.ICEFilename = filepath.Join(dataDir, "ice-servers.json") + ice.ICEFilename = filepath.Join(group.DataDirectory, "ice-servers.json") // make sure the list of public groups is updated early go group.Update() @@ -123,7 +121,7 @@ serverDone := make(chan struct{}) go func() { - err := webserver.Serve(httpAddr, dataDir) + err := webserver.Serve(httpAddr, group.DataDirectory) if err != nil { log.Printf("Server: %v", err) } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/go.mod new/galene-0.4.1/go.mod --- old/galene-0.4/go.mod 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/go.mod 2021-11-05 15:31:09.000000000 +0100 @@ -7,13 +7,12 @@ github.com/gorilla/websocket v1.4.2 github.com/jech/cert v0.0.0-20210819231831-aca735647728 github.com/jech/samplebuilder v0.0.0-20210823163459-dd92d75bae48 - github.com/pion/ice/v2 v2.1.12 - github.com/pion/rtcp v1.2.6 - github.com/pion/rtp v1.7.2 + github.com/pion/ice/v2 v2.1.13 + github.com/pion/rtcp v1.2.8 + github.com/pion/rtp v1.7.4 github.com/pion/sdp/v3 v3.0.4 github.com/pion/turn/v2 v2.0.5 - github.com/pion/webrtc/v3 v3.1.0-beta.6 - golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 - golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f // indirect - golang.org/x/sys v0.0.0-20210903071746-97244b99971b + github.com/pion/webrtc/v3 v3.1.6 + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 + golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 ) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/go.sum new/galene-0.4.1/go.sum --- old/galene-0.4/go.sum 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/go.sum 2021-11-05 15:31:09.000000000 +0100 @@ -42,25 +42,29 @@ github.com/onsi/gomega v1.11.0/go.mod h1:azGKhqFUon9Vuj0YmTfLSmx0FUwqXYSTl5re8lQLTUg= github.com/pion/datachannel v1.4.21 h1:3ZvhNyfmxsAqltQrApLPQMhSFNA+aT87RqyCq4OXmf0= github.com/pion/datachannel v1.4.21/go.mod h1:oiNyP4gHx2DIwRzX/MFyH0Rz/Gz05OgBlayAI2hAWjg= -github.com/pion/dtls/v2 v2.0.9 h1:7Ow+V++YSZQMYzggI0P9vLJz/hUFcffsfGMfT/Qy+u8= github.com/pion/dtls/v2 v2.0.9/go.mod h1:O0Wr7si/Zj5/EBFlDzDd6UtVxx25CE1r7XM7BQKYQho= +github.com/pion/dtls/v2 v2.0.10 h1:wgys7gPR1NMbWjmjJ3CW7lkUGaun8djgH8nahpNLnxI= +github.com/pion/dtls/v2 v2.0.10/go.mod h1:00OxfeCRWHShcqT9jx8pKKmBWuTt0NCZoVPCaC4VKvU= github.com/pion/ice/v2 v2.1.10/go.mod h1:kV4EODVD5ux2z8XncbLHIOtcXKtYXVgLVCeVqnpoeP0= -github.com/pion/ice/v2 v2.1.12 h1:ZDBuZz+fEI7iDifZCYFVzI4p0Foy0YhdSSZ87ZtRcRE= -github.com/pion/ice/v2 v2.1.12/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= -github.com/pion/interceptor v0.0.15 h1:pQFkBUL8akUHiGoFr+pM94Q/15x7sLFh0K3Nj+DCC6s= +github.com/pion/ice/v2 v2.1.13 h1:/YNYcIw56LT/whwuzkTnrprcRnapj2ZNqUsR0W8elmo= +github.com/pion/ice/v2 v2.1.13/go.mod h1:ovgYHUmwYLlRvcCLI67PnQ5YGe+upXZbGgllBDG/ktU= github.com/pion/interceptor v0.0.15/go.mod h1:pg3J253eGi5bqyKzA74+ej5Y19ez2jkWANVnF+Z9Dfk= +github.com/pion/interceptor v0.1.0 h1:SlXKaDlEvSl7cr4j8fJykzVz4UdH+7UDtcvx+u01wLU= +github.com/pion/interceptor v0.1.0/go.mod h1:j5NIl3tJJPB3u8+Z2Xz8MZs/VV6rc+If9mXEKNuFmEM= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= github.com/pion/mdns v0.0.5 h1:Q2oj/JB3NqfzY9xGZ1fPzZzK7sDSD8rZPOvcIQ10BCw= github.com/pion/mdns v0.0.5/go.mod h1:UgssrvdD3mxpi8tMxAXbsppL3vJ4Jipw1mTCW+al01g= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.6 h1:1zvwBbyd0TeEuuWftrd/4d++m+/kZSeiguxU61LFWpo= github.com/pion/rtcp v1.2.6/go.mod h1:52rMNPWFsjr39z9B9MhnkqhPLoeHTv1aN63o/42bWE0= +github.com/pion/rtcp v1.2.8 h1:Cys8X6r0xxU65ESTmXkqr8eU1Q1Wx+lNkoZCUH4zD7E= +github.com/pion/rtcp v1.2.8/go.mod h1:qVPhiCzAm4D/rxb6XzKeyZiQK69yJpbUDJSF7TgrqNo= github.com/pion/rtp v1.7.0/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/rtp v1.7.1/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= -github.com/pion/rtp v1.7.2 h1:HCDKDCixh7PVjkQTsqHAbk1lg+bx059EHxcnyl42dYs= github.com/pion/rtp v1.7.2/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= +github.com/pion/rtp v1.7.4 h1:4dMbjb1SuynU5OpA3kz1zHK+u+eOCQjW3MAeVHf1ODA= +github.com/pion/rtp v1.7.4/go.mod h1:bDb5n+BFZxXx0Ea7E5qe+klMuqiBrP+w8XSjiWtCUko= github.com/pion/sctp v1.7.10/go.mod h1:EhpTUQu1/lcK3xI+eriS6/96fWetHGCvBi9MSsnaBN0= github.com/pion/sctp v1.7.12 h1:GsatLufywVruXbZZT1CKg+Jr8ZTkwiPnmUC/oO9+uuY= github.com/pion/sctp v1.7.12/go.mod h1:xFe9cLMZ5Vj6eOzpyiKjT9SwGM4KpK/8Jbw5//jc+0s= @@ -79,8 +83,8 @@ github.com/pion/udp v0.1.1 h1:8UAPvyqmsxK8oOjloDk4wUt63TzFe9WEJkg5lChlj7o= github.com/pion/udp v0.1.1/go.mod h1:6AFo+CMdKQm7UiA0eUPA8/eVCTx8jBIITLZHc9DWX5M= github.com/pion/webrtc/v3 v3.1.0-beta.3/go.mod h1:I4O6v2pkiXdVmcn7sUhCNwHUAepGU19PVEyR204s1qc= -github.com/pion/webrtc/v3 v3.1.0-beta.6 h1:HAEH0dObc+g0BleuMSow+rE4jpXYhJjmSchnmJFFso8= -github.com/pion/webrtc/v3 v3.1.0-beta.6/go.mod h1:KQH/wVKKJzBTQ6sX1bDTxsvTpQ6gEjVZSJlzYaB58aM= +github.com/pion/webrtc/v3 v3.1.6 h1:r6WQRayW2SyKTYeRl4vBUQ43XXp7RSwBJ9+tNQWI5zQ= +github.com/pion/webrtc/v3 v3.1.6/go.mod h1:tkwdWNYdZhc200hH/wPx6AtNo/rcTAM6MICA6dg1je8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= @@ -96,8 +100,8 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 h1:HWj/xjIHfjYU5nVXpTM0s39J9CbLn7Cc5a7IC5rwsMQ= -golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -112,9 +116,9 @@ golang.org/x/net v0.0.0-20210331212208-0fccb6fa2b5c/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f h1:w6wWR0H+nyVpbSAQbzVEIACVyr/h8l/BEkY6Sokc7Eg= -golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211020060615-d418f374d309 h1:A0lJIi+hcTR6aajJH4YqKWwohY4aW9RO7oRMcdv+HKI= +golang.org/x/net v0.0.0-20211020060615-d418f374d309/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -131,8 +135,8 @@ golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b h1:3Dq0eVHn0uaQJmPO+/aYPI/fRMqdrVDbu7MQcku54gg= -golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359 h1:2B5p2L5IfGiD7+b9BOoRMC6DgObAVZV+Fsp050NqXik= +golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/group/client.go new/galene-0.4.1/group/client.go --- old/galene-0.4/group/client.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/group/client.go 2021-11-05 15:31:09.000000000 +0100 @@ -76,7 +76,7 @@ return json.Marshal(RawPassword(p)) } -type ClientCredentials struct { +type ClientPattern struct { Username string `json:"username,omitempty"` Password *Password `json:"password,omitempty"` } @@ -88,15 +88,16 @@ System bool `json:"system,omitempty"` } -type Challengeable interface { - Username() string - Challenge(string, ClientCredentials) bool +type ClientCredentials struct { + System bool + Username string + Password string } type Client interface { Group() *Group Id() string - Challengeable + Username() string Permissions() ClientPermissions SetPermissions(ClientPermissions) Status() map[string]interface{} diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/group/group.go new/galene-0.4.1/group/group.go --- old/galene-0.4/group/group.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/group/group.go 2021-11-05 15:31:09.000000000 +0100 @@ -17,11 +17,12 @@ "github.com/pion/webrtc/v3" ) -var Directory string +var Directory, DataDirectory string var UseMDNS bool var UDPMin, UDPMax uint16 var ErrNotAuthorised = errors.New("not authorised") +var ErrAnonymousNotAuthorised = errors.New("anonymous users not authorised in this group") type UserError string @@ -105,28 +106,16 @@ } } -func (g *Group) Public() bool { +func (g *Group) Description() *Description { g.mu.Lock() defer g.mu.Unlock() - return g.description.Public + return g.description } -func (g *Group) Redirect() string { +func (g *Group) ClientCount() int { g.mu.Lock() defer g.mu.Unlock() - return g.description.Redirect -} - -func (g *Group) AllowRecording() bool { - g.mu.Lock() - defer g.mu.Unlock() - return g.description.AllowRecording -} - -func (g *Group) DisplayName() string { - g.mu.Lock() - defer g.mu.Unlock() - return g.description.DisplayName + return len(g.clients) } func (g *Group) EmptyTime() time.Duration { @@ -495,7 +484,9 @@ func Get(name string) *Group { groups.mu.Lock() defer groups.mu.Unlock() - + if groups.groups == nil { + return nil + } return groups.groups[name] } @@ -522,7 +513,7 @@ return true } -func AddClient(group string, c Client) (*Group, error) { +func AddClient(group string, c Client, creds ClientCredentials) (*Group, error) { g, err := Add(group, nil) if err != nil { return nil, err @@ -534,7 +525,7 @@ clients := g.getClientsUnlocked(nil) if !c.Permissions().System { - perms, err := g.description.GetPermission(group, c) + perms, err := g.description.GetPermission(group, creds) if err != nil { return nil, err } @@ -698,8 +689,12 @@ }) } -func (g *Group) Shutdown(message string) { - kickall(g, message) +func Shutdown(message string) { + Range(func(g *Group) bool { + g.SetLocked(true, message) + kickall(g, message) + return true + }) } type warner interface { @@ -781,12 +776,16 @@ return h } -func matchClient(group string, c Challengeable, users []ClientCredentials) (bool, bool) { +func matchClient(group string, creds ClientCredentials, users []ClientPattern) (bool, bool) { matched := false for _, u := range users { - if u.Username == c.Username() { + if u.Username == creds.Username { matched = true - if c.Challenge(group, u) { + if u.Password == nil { + return true, true + } + m, _ := u.Password.Match(creds.Password) + if m { return true, true } } @@ -797,7 +796,11 @@ for _, u := range users { if u.Username == "" { - if c.Challenge(group, u) { + if u.Password == nil { + return true, true + } + m, _ := u.Password.Match(creds.Password) + if m { return true, true } } @@ -805,8 +808,71 @@ return false, false } -// Type Description represents a group description together with some -// metadata about the JSON file it was deserialised from. +// Configuration represents the contents of the data/config.json file. +type Configuration struct { + // The modtime and size of the file. These are used to detect + // when a file has changed on disk. + modTime time.Time `json:"-"` + fileSize int64 `json:"-"` + + CanonicalHost string `json:"canonicalHost"` + Admin []ClientPattern `json:"admin"` +} + +func (conf Configuration) Zero() bool { + return conf.modTime.Equal(time.Time{}) && + conf.fileSize == 0 +} + +var configuration struct { + mu sync.Mutex + configuration *Configuration +} + +func GetConfiguration() (*Configuration, error) { + configuration.mu.Lock() + defer configuration.mu.Unlock() + + if configuration.configuration == nil { + configuration.configuration = &Configuration{} + } + + filename := filepath.Join(DataDirectory, "config.json") + fi, err := os.Stat(filename) + if err != nil { + if os.IsNotExist(err) { + if !configuration.configuration.Zero() { + configuration.configuration = &Configuration{} + } + return configuration.configuration, nil + } + return nil, err + } + + if configuration.configuration.modTime.Equal(fi.ModTime()) && + configuration.configuration.fileSize == fi.Size() { + return configuration.configuration, nil + } + + f, err := os.Open(filename) + if err != nil { + return nil, err + } + defer f.Close() + + d := json.NewDecoder(f) + d.DisallowUnknownFields() + var conf Configuration + err = d.Decode(&conf) + if err != nil { + return nil, err + } + configuration.configuration = &conf + return configuration.configuration, nil +} + +// Description represents a group description together with some metadata +// about the JSON file it was deserialised from. type Description struct { // The file this was deserialised from. This is not necessarily // the name of the group, for example in case of a subgroup. @@ -858,13 +924,13 @@ Autokick bool `json:"autokick,omitempty"` // A list of logins for ops. - Op []ClientCredentials `json:"op,omitempty"` + Op []ClientPattern `json:"op,omitempty"` // A list of logins for presenters. - Presenter []ClientCredentials `json:"presenter,omitempty"` + Presenter []ClientPattern `json:"presenter,omitempty"` // A list of logins for non-presenting users. - Other []ClientCredentials `json:"other,omitempty"` + Other []ClientPattern `json:"other,omitempty"` // Codec preferences. If empty, a suitable default is chosen in // the APIFromNames function. @@ -963,12 +1029,12 @@ return &desc, nil } -func (desc *Description) GetPermission(group string, c Challengeable) (ClientPermissions, error) { +func (desc *Description) GetPermission(group string, creds ClientCredentials) (ClientPermissions, error) { var p ClientPermissions - if !desc.AllowAnonymous && c.Username() == "" { - return p, UserError("anonymous users not allowed in this group, please choose a username") + if !desc.AllowAnonymous && creds.Username == "" { + return p, ErrAnonymousNotAuthorised } - if found, good := matchClient(group, c, desc.Op); found { + if found, good := matchClient(group, creds, desc.Op); found { if good { p.Op = true p.Present = true @@ -979,14 +1045,14 @@ } return p, ErrNotAuthorised } - if found, good := matchClient(group, c, desc.Presenter); found { + if found, good := matchClient(group, creds, desc.Presenter); found { if good { p.Present = true return p, nil } return p, ErrNotAuthorised } - if found, good := matchClient(group, c, desc.Other); found { + if found, good := matchClient(group, creds, desc.Other); found { if good { return p, nil } @@ -995,26 +1061,37 @@ return p, ErrNotAuthorised } -type Public struct { +type Status struct { Name string `json:"name"` DisplayName string `json:"displayName,omitempty"` Description string `json:"description,omitempty"` Locked bool `json:"locked,omitempty"` - ClientCount int `json:"clientCount"` + ClientCount *int `json:"clientCount,omitempty"` } -func GetPublic() []Public { - gs := make([]Public, 0) +func GetStatus(g *Group, authentified bool) Status { + desc := g.Description() + d := Status{ + Name: g.name, + DisplayName: desc.DisplayName, + Description: desc.Description, + } + + if authentified || desc.Public { + // these are considered private information + locked, _ := g.Locked() + count := g.ClientCount() + d.Locked = locked + d.ClientCount = &count + } + return d +} + +func GetPublic() []Status { + gs := make([]Status, 0) Range(func(g *Group) bool { - if g.Public() { - locked, _ := g.Locked() - gs = append(gs, Public{ - Name: g.name, - DisplayName: g.DisplayName(), - Description: g.description.Description, - Locked: locked, - ClientCount: len(g.clients), - }) + if g.Description().Public { + gs = append(gs, GetStatus(g, false)) } return true }) @@ -1028,6 +1105,14 @@ // list of public groups. It also removes from memory any non-public // groups that haven't been accessed in maxHistoryAge. func Update() { + _, err := GetConfiguration() + if err != nil { + log.Printf("%v: %v", + filepath.Join(DataDirectory, "config.json"), + err, + ) + } + names := GetNames() for _, name := range names { @@ -1048,7 +1133,7 @@ } } - err := filepath.Walk( + err = filepath.Walk( Directory, func(path string, fi os.FileInfo, err error) error { if err != nil { @@ -1070,6 +1155,11 @@ ) return nil } + base := filepath.Base(filename) + if base[0] == '.' { + log.Printf("Group file %v ignored", filename) + return nil + } name := filename[:len(filename)-5] desc, err := GetDescription(name) if err != nil { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/group/group_test.go new/galene-0.4.1/group/group_test.go --- old/galene-0.4/group/group_test.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/group/group_test.go 2021-11-05 15:31:09.000000000 +0100 @@ -29,18 +29,6 @@ if locked, _ := g.Locked(); locked { t.Errorf("Locked: expected false, got %v", locked) } - if public := g.Public(); public { - t.Errorf("Public: expected false, got %v", public) - } - if public := g2.Public(); !public { - t.Errorf("Public: expected true, got %v", public) - } - if redirect := g.Redirect(); redirect != "" { - t.Errorf("Redirect: expected empty, got %v", redirect) - } - if ar := g.AllowRecording(); ar { - t.Errorf("Allow Recording: expected false, got %v", ar) - } api, err := g.API() if err != nil || api == nil { t.Errorf("Couldn't get API: %v", err) @@ -136,56 +124,36 @@ } } -type testClient struct { - username string - password string +var badClients = []ClientCredentials{ + {Username: "jch", Password: "foo"}, + {Username: "john", Password: "foo"}, + {Username: "james", Password: "foo"}, } -func (c testClient) Username() string { - return c.username -} - -func (c testClient) Challenge(g string, creds ClientCredentials) bool { - if creds.Password == nil { - return true - } - m, err := creds.Password.Match(c.password) - if err != nil { - return false - } - return m -} - -type testClientPerm struct { - c testClient +type credPerm struct { + c ClientCredentials p ClientPermissions } -var badClients = []testClient{ - testClient{"jch", "foo"}, - testClient{"john", "foo"}, - testClient{"james", "foo"}, -} - -var goodClients = []testClientPerm{ +var goodClients = []credPerm{ { - testClient{"jch", "topsecret"}, + ClientCredentials{Username: "jch", Password: "topsecret"}, ClientPermissions{Op: true, Present: true}, }, { - testClient{"john", "secret"}, + ClientCredentials{Username: "john", Password: "secret"}, ClientPermissions{Present: true}, }, { - testClient{"john", "secret2"}, + ClientCredentials{Username: "john", Password: "secret2"}, ClientPermissions{Present: true}, }, { - testClient{"james", "secret3"}, + ClientCredentials{Username: "james", Password: "secret3"}, ClientPermissions{}, }, { - testClient{"paul", "secret3"}, + ClientCredentials{Username: "paul", Password: "secret3"}, ClientPermissions{}, }, } @@ -198,7 +166,7 @@ } for _, c := range badClients { - t.Run("bad "+c.Username(), func(t *testing.T) { + t.Run("bad "+c.Username, func(t *testing.T) { p, err := d.GetPermission("test", c) if err != ErrNotAuthorised { t.Errorf("GetPermission %v: %v %v", c, err, p) @@ -207,7 +175,7 @@ } for _, cp := range goodClients { - t.Run("good "+cp.c.Username(), func(t *testing.T) { + t.Run("good "+cp.c.Username, func(t *testing.T) { p, err := d.GetPermission("test", cp.c) if err != nil { t.Errorf("GetPermission %v: %v", cp.c, err) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/rtpconn/rtpconn.go new/galene-0.4.1/rtpconn/rtpconn.go --- old/galene-0.4/rtpconn/rtpconn.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/rtpconn/rtpconn.go 2021-11-05 15:31:09.000000000 +0100 @@ -508,8 +508,6 @@ id string client group.Client label string - userId string - username string pc *webrtc.PeerConnection iceCandidates []*webrtc.ICECandidateInit @@ -548,7 +546,7 @@ } func (up *rtpUpConnection) User() (string, string) { - return up.userId, up.username + return up.client.Id(), up.client.Username() } func (up *rtpUpConnection) AddLocal(local conn.Down) error { @@ -1003,7 +1001,7 @@ if rate < ^uint64(0) && len(ssrcs) > 0 { packets = append(packets, &rtcp.ReceiverEstimatedMaximumBitrate{ - Bitrate: rate, + Bitrate: float32(rate), SSRCs: ssrcs, }, ) @@ -1185,7 +1183,8 @@ track.remote.RequestKeyframe() } case *rtcp.ReceiverEstimatedMaximumBitrate: - track.maxREMBBitrate.Set(p.Bitrate, jiffies) + rate := uint64(p.Bitrate + 0.5) + track.maxREMBBitrate.Set(rate, jiffies) adjust = true case *rtcp.ReceiverReport: for _, r := range p.Reports { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/rtpconn/rtpconn_test.go new/galene-0.4.1/rtpconn/rtpconn_test.go --- old/galene-0.4/rtpconn/rtpconn_test.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/rtpconn/rtpconn_test.go 2021-11-05 15:31:09.000000000 +0100 @@ -37,4 +37,3 @@ t.Errorf("Expected %v, got %v", info, info2) } } - diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/rtpconn/webclient.go new/galene-0.4.1/rtpconn/webclient.go --- old/galene-0.4/rtpconn/webclient.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/rtpconn/webclient.go 2021-11-05 15:31:09.000000000 +0100 @@ -56,7 +56,6 @@ group *group.Group id string username string - password string permissions group.ClientPermissions status map[string]interface{} requested map[string][]string @@ -83,18 +82,6 @@ return c.username } -func (c *webClient) Challenge(group string, creds group.ClientCredentials) bool { - if creds.Password == nil { - return true - } - m, err := creds.Password.Match(c.password) - if err != nil { - log.Printf("Password match: %v", err) - return false - } - return m -} - func (c *webClient) Permissions() group.ClientPermissions { return c.permissions } @@ -124,7 +111,7 @@ Password string `json:"password,omitempty"` Privileged bool `json:"privileged,omitempty"` Permissions *group.ClientPermissions `json:"permissions,omitempty"` - Status map[string]interface{} `json:"status,omitempty"` + Status interface{} `json:"status,omitempty"` Group string `json:"group,omitempty"` Value interface{} `json:"value,omitempty"` NoEcho bool `json:"noecho,omitempty"` @@ -211,9 +198,12 @@ c.mu.Unlock() return os.ErrNotExist } - if userId != "" && conn.userId != userId { - c.mu.Unlock() - return ErrUserMismatch + if userId != "" { + id, _ := conn.User() + if id != userId { + c.mu.Unlock() + return ErrUserMismatch + } } replace := conn.getReplace(false) @@ -365,7 +355,7 @@ id = remoteTrack.track.Kind().String() } msid := remoteTrack.track.StreamID() - if msid == "" { + if msid == "" || msid == "-" { log.Println("Got track with empty msid") msid = remoteTrack.conn.Label() } @@ -576,8 +566,6 @@ return err } - up.userId = c.Id() - up.username = c.Username() if replace != "" { up.replace = replace delUpConn(c, replace, c.Id(), false) @@ -825,21 +813,6 @@ return nil } -func getGroupStatus(g *group.Group) map[string]interface{} { - status := make(map[string]interface{}) - if locked, message := g.Locked(); locked { - if message == "" { - status["locked"] = true - } else { - status["locked"] = message - } - } - if dn := g.DisplayName(); dn != "" { - status["displayName"] = dn - } - return status -} - func readMessage(conn *websocket.Conn, m *clientMessage) error { err := conn.SetReadDeadline(time.Now().Add(15 * time.Second)) if err != nil { @@ -1132,11 +1105,11 @@ Status: a.status, }) case joinedAction: - var status map[string]interface{} + var status interface{} if a.group != "" { g := group.Get(a.group) if g != nil { - status = getGroupStatus(g) + status = group.GetStatus(g, true) } } perms := c.permissions @@ -1161,7 +1134,7 @@ Group: g.Name(), Username: c.username, Permissions: &perms, - Status: getGroupStatus(g), + Status: group.GetStatus(g, true), RTCConfiguration: ice.ICEConfiguration(), }) if !c.permissions.Present { @@ -1277,7 +1250,7 @@ switch perm { case "op": c.permissions.Op = true - if g.AllowRecording() { + if g.Description().AllowRecording { c.permissions.Record = true } case "unop": @@ -1342,8 +1315,12 @@ return group.ProtocolError("cannot join multiple groups") } c.username = m.Username - c.password = m.Password - g, err := group.AddClient(m.Group, c) + g, err := group.AddClient(m.Group, c, + group.ClientCredentials{ + Username: m.Username, + Password: m.Password, + }, + ) if err != nil { var s string if os.IsNotExist(err) { @@ -1351,6 +1328,8 @@ } else if err == group.ErrNotAuthorised { s = "not authorised" time.Sleep(200 * time.Millisecond) + } else if err == group.ErrAnonymousNotAuthorised { + s = "please choose a username" } else if e, ok := err.(group.UserError); ok { s = string(e) } else { @@ -1366,7 +1345,7 @@ Value: s, }) } - if redirect := g.Redirect(); redirect != "" { + if redirect := g.Description().Redirect; redirect != "" { // We normally redirect at the HTTP level, but the group // description could have been edited in the meantime. return c.write(clientMessage{ @@ -1580,7 +1559,11 @@ } } disk := diskwriter.New(g) - _, err := group.AddClient(g.Name(), disk) + _, err := group.AddClient(g.Name(), disk, + group.ClientCredentials{ + System: true, + }, + ) if err != nil { disk.Close() return c.error(err) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/static/galene.css new/galene-0.4.1/static/galene.css --- old/galene-0.4/static/galene.css 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/static/galene.css 2021-11-05 15:31:09.000000000 +0100 @@ -583,6 +583,7 @@ position: relative; display: flex; justify-content: center; + overflow: scroll; } .login-box { @@ -912,7 +913,7 @@ } .media-failed { - opacity: 0.7; + filter: grayscale(0.5) contrast(0.5); } .mirror { @@ -992,7 +993,7 @@ padding: 10px; background: #fff; height: calc(100% - 56px); - overflow-y: scroll; + overflow-y: auto; overflow-x: hidden; } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/static/galene.js new/galene-0.4.1/static/galene.js --- old/galene-0.4/static/galene.js 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/static/galene.js 2021-11-05 15:31:09.000000000 +0100 @@ -26,52 +26,11 @@ /** @type {ServerConnection} */ let serverConnection; -/** - * @typedef {Object} userpass - * @property {string} username - * @property {string} password - */ - -/* Some browsers disable session storage when cookies are disabled, - we fall back to a global variable. */ -/** - * @type {userpass} - */ -let fallbackUserPass = null; +/** @type {Object} */ +let groupStatus = {}; - -/** - * @param {string} username - * @param {string} password - */ -function storeUserPass(username, password) { - let userpass = {username: username, password: password}; - try { - window.sessionStorage.setItem('userpass', JSON.stringify(userpass)); - fallbackUserPass = null; - } catch(e) { - console.warn("Couldn't store password:", e); - fallbackUserPass = userpass; - } -} - -/** - * Returns null if the user hasn't logged in yet. - * - * @returns {userpass} - */ -function getUserPass() { - /** @type{userpass} */ - let userpass; - try { - let json = window.sessionStorage.getItem('userpass'); - userpass = JSON.parse(json); - } catch(e) { - console.warn("Couldn't retrieve password:", e); - userpass = fallbackUserPass; - } - return userpass || null; -} +/** @type {string} */ +let username = null; /** * @typedef {Object} settings @@ -288,14 +247,6 @@ scheduleReconsiderDownRate(); } -function fillLogin() { - let userpass = getUserPass(); - getInputElement('username').value = - userpass ? userpass.username : ''; - getInputElement('password').value = - userpass ? userpass.password : ''; -} - /** * @param{boolean} connected */ @@ -311,7 +262,6 @@ scheduleReconsiderDownRate(); } } else { - fillLogin(); userbox.classList.add('invisible'); connectionbox.classList.remove('invisible'); displayError('Disconnected', 'error'); @@ -322,9 +272,17 @@ /** @this {ServerConnection} */ function gotConnected() { + username = getInputElement('username').value.trim(); setConnected(true); - let up = getUserPass(); - this.join(group, up.username, up.password); + try { + let pw = getInputElement('password').value; + getInputElement('password').value = ''; + this.join(group, username, pw); + } catch(e) { + console.error(e); + displayError(e); + serverConnection.close(); + } } /** @@ -2077,10 +2035,8 @@ } function displayUsername() { - let userpass = getUserPass(); + document.getElementById('userspan').textContent = username; let text = ''; - if(userpass && userpass.username) - document.getElementById('userspan').textContent = userpass.username; if(serverConnection.permissions.op && serverConnection.permissions.present) text = '(op, presenter)'; else if(serverConnection.permissions.op) @@ -2111,7 +2067,8 @@ } if(title) set(title); - set('Gal??ne'); + else + set('Gal??ne'); } @@ -2142,6 +2099,7 @@ return; case 'join': case 'change': + groupStatus = status; setTitle((status && status.displayName) || capitalise(group)); displayUsername(); setButtonsVisibility(); @@ -2986,10 +2944,7 @@ return; connecting = true; try { - let username = getInputElement('username').value.trim(); - let password = getInputElement('password').value; - storeUserPass(username, password); - serverConnect(); + await serverConnect(); } finally { connecting = false; } @@ -3088,13 +3043,25 @@ } } -function start() { - group = decodeURIComponent(location.pathname.replace(/^\/[a-z]*\//, '')); - setTitle(capitalise(group)); +async function start() { + group = decodeURIComponent( + location.pathname.replace(/^\/[a-z]*\//, '').replace(/\/$/, '') + ); + /** @type {Object} */ + try { + let r = await fetch(".status.json") + if(!r.ok) + throw new Error(`${r.status} ${r.statusText}`); + groupStatus = await r.json() + } catch(e) { + console.error(e); + return; + } + + setTitle(groupStatus.displayName || capitalise(group)); addFilters(); setMediaChoices(false).then(e => reflectSettings()); - fillLogin(); document.getElementById("login-container").classList.remove('invisible'); setViewportHeight(); } diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/static/mainpage.js new/galene-0.4.1/static/mainpage.js --- old/galene-0.4/static/mainpage.js 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/static/mainpage.js 2021-11-05 15:31:09.000000000 +0100 @@ -24,7 +24,7 @@ e.preventDefault(); let group = document.getElementById('group').value.trim(); if(group !== '') - location.href = '/group/' + group; + location.href = '/group/' + group + '/'; }; async function listPublicGroups() { @@ -59,8 +59,8 @@ let tr = document.createElement('tr'); let td = document.createElement('td'); let a = document.createElement('a'); - a.textContent = group.name; - a.href = '/group/' + encodeURIComponent(group.displayName || group.name); + a.textContent = group.displayName || group.name; + a.href = '/group/' + group.name + '/'; td.appendChild(a); tr.appendChild(td); let td2 = document.createElement('td'); diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/static/protocol.js new/galene-0.4.1/static/protocol.js --- old/galene-0.4/static/protocol.js 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/static/protocol.js 2021-11-05 15:31:09.000000000 +0100 @@ -677,6 +677,10 @@ }; c.pc.ontrack = function(e) { + if(e.streams.length < 1) { + console.error("Got track with no stream"); + return; + } c.stream = e.streams[0]; let changed = recomputeUserStreams(sc, source, c); if(c.ondowntrack) { diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/galene-0.4/webserver/webserver.go new/galene-0.4.1/webserver/webserver.go --- old/galene-0.4/webserver/webserver.go 2021-09-05 18:35:19.000000000 +0200 +++ new/galene-0.4.1/webserver/webserver.go 2021-11-05 15:31:09.000000000 +0100 @@ -1,7 +1,6 @@ package webserver import ( - "bufio" "context" "crypto/tls" "encoding/json" @@ -33,8 +32,6 @@ var StaticRoot string -var Redirect string - var Insecure bool func Serve(address string, dataDir string) error { @@ -70,10 +67,7 @@ } } s.RegisterOnShutdown(func() { - group.Range(func(g *group.Group) bool { - go g.Shutdown("server is shutting down") - return true - }) + group.Shutdown("server is shutting down") }) server.Store(s) @@ -134,13 +128,18 @@ ) func redirect(w http.ResponseWriter, r *http.Request) bool { - if Redirect == "" || strings.EqualFold(r.Host, Redirect) { + conf, err := group.GetConfiguration() + if err != nil || conf.CanonicalHost == "" { + return false + } + + if strings.EqualFold(r.Host, conf.CanonicalHost) { return false } u := url.URL{ Scheme: "https", - Host: Redirect, + Host: conf.CanonicalHost, Path: r.URL.Path, } http.Redirect(w, r, u.String(), http.StatusMovedPermanently) @@ -265,6 +264,10 @@ return "" } + if name[0] == '.' { + return "" + } + if filepath.Separator != '/' && strings.ContainsRune(name, filepath.Separator) { return "" @@ -279,15 +282,49 @@ return } - mungeHeader(w) + if strings.HasSuffix(r.URL.Path, "/.status.json") { + groupStatusHandler(w, r) + return + } + name := parseGroupName("/group/", r.URL.Path) if name == "" { notFound(w) return } - if r.URL.Path != "/group/"+name { - http.Redirect(w, r, "/group/"+name, http.StatusPermanentRedirect) + g, err := group.Add(name, nil) + if err != nil { + if os.IsNotExist(err) { + notFound(w) + } else { + log.Printf("group.Add: %v", err) + http.Error(w, "Internal server error", + http.StatusInternalServerError) + } + return + } + + if r.URL.Path != "/group/"+name+"/" { + http.Redirect(w, r, "/group/"+name+"/", + http.StatusPermanentRedirect) + return + } + + if redirect := g.Description().Redirect; redirect != "" { + http.Redirect(w, r, redirect, http.StatusPermanentRedirect) + return + } + + mungeHeader(w) + serveFile(w, r, filepath.Join(StaticRoot, "galene.html")) +} + +func groupStatusHandler(w http.ResponseWriter, r *http.Request) { + path := path.Dir(r.URL.Path) + name := parseGroupName("/group/", path) + if name == "" { + notFound(w) return } @@ -296,19 +333,23 @@ if os.IsNotExist(err) { notFound(w) } else { - log.Printf("addGroup: %v", err) http.Error(w, "Internal server error", http.StatusInternalServerError) } return } - if redirect := g.Redirect(); redirect != "" { - http.Redirect(w, r, redirect, http.StatusPermanentRedirect) + d := group.GetStatus(g, false) + w.Header().Set("content-type", "application/json") + w.Header().Set("cache-control", "no-cache") + + if r.Method == "HEAD" { return } - serveFile(w, r, filepath.Join(StaticRoot, "galene.html")) + e := json.NewEncoder(w) + e.Encode(d) + return } func publicHandler(w http.ResponseWriter, r *http.Request) { @@ -325,26 +366,20 @@ return } -func getPassword(dataDir string) (string, string, error) { - f, err := os.Open(filepath.Join(dataDir, "passwd")) - if err != nil { - return "", "", err - } - defer f.Close() - - r := bufio.NewReader(f) - - s, err := r.ReadString('\n') +func adminMatch(username, password string) (bool, error) { + conf, err := group.GetConfiguration() if err != nil { - return "", "", err + return false, err } - l := strings.SplitN(strings.TrimSpace(s), ":", 2) - if len(l) != 2 { - return "", "", errors.New("couldn't parse passwords") + for _, cred := range conf.Admin { + if cred.Username == "" || cred.Username == username { + if ok, _ := cred.Password.Match(password); ok { + return true, nil + } + } } - - return l[0], l[1], nil + return false, nil } func failAuthentication(w http.ResponseWriter, realm string) { @@ -354,15 +389,16 @@ } func statsHandler(w http.ResponseWriter, r *http.Request, dataDir string) { - u, p, err := getPassword(dataDir) - if err != nil { - log.Printf("Passwd: %v", err) + username, password, ok := r.BasicAuth() + if !ok { failAuthentication(w, "stats") return } - username, password, ok := r.BasicAuth() - if !ok || username != u || password != p { + if ok, err := adminMatch(username, password); !ok { + if err != nil { + log.Printf("Administrator password: %v", err) + } failAuthentication(w, "stats") return } @@ -375,7 +411,7 @@ ss := stats.GetGroups() e := json.NewEncoder(w) - err = e.Encode(ss) + err := e.Encode(ss) if err != nil { log.Printf("stats.json: %v", err) } @@ -536,27 +572,6 @@ } } -type httpClient struct { - username string - password string -} - -func (c httpClient) Username() string { - return c.username -} - -func (c httpClient) Challenge(group string, creds group.ClientCredentials) bool { - if creds.Password == nil { - return true - } - m, err := creds.Password.Match(c.password) - if err != nil { - log.Printf("Password match: %v", err) - return false - } - return m -} - func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname string) bool { desc, err := group.GetDescription(groupname) if err != nil { @@ -568,7 +583,12 @@ return false } - p, err := desc.GetPermission(groupname, httpClient{user, pass}) + p, err := desc.GetPermission(groupname, + group.ClientCredentials{ + Username: user, + Password: pass, + }, + ) if err != nil || !p.Record { if err == group.ErrNotAuthorised { time.Sleep(200 * time.Millisecond) ++++++ vendor.tar.gz ++++++ ++++ 14430 lines of diff (skipped)