Commit 29bd67cc authored by Juliusz Chroboczek's avatar Juliusz Chroboczek

Implement subgroups.

parent dcfd071e
...@@ -119,6 +119,8 @@ fields, all of which are optional. ...@@ -119,6 +119,8 @@ fields, all of which are optional.
kept (default 14400, i.e. 4 hours); kept (default 14400, i.e. 4 hours);
- `allow-recording`: if true, then recording is allowed in this group; - `allow-recording`: if true, then recording is allowed in this group;
- `allow-anonymous`: if true, then users may connect with an empty username. - `allow-anonymous`: if true, then users may connect with an empty username.
- `allow-subgroups`: if true, then subgroups of the form `group/subgroup`
are automatically created when accessed.
- `redirect`: if set, then attempts to join the group will be redirected - `redirect`: if set, then attempts to join the group will be redirected
to the given URL; most other fields are ignored in this case. to the given URL; most other fields are ignored in this case.
......
...@@ -9,6 +9,7 @@ import ( ...@@ -9,6 +9,7 @@ import (
"encoding/json" "encoding/json"
"log" "log"
"os" "os"
"path"
"path/filepath" "path/filepath"
"sort" "sort"
"strings" "strings"
...@@ -198,16 +199,7 @@ func Add(name string, desc *description) (*Group, error) { ...@@ -198,16 +199,7 @@ func Add(name string, desc *description) (*Group, error) {
} }
if g.dead || time.Since(g.description.loadTime) > 5*time.Second { if g.dead || time.Since(g.description.loadTime) > 5*time.Second {
changed, err := descriptionChanged(name, g.description) if descriptionChanged(name, g.description) {
if err != nil {
if !os.IsNotExist(err) {
log.Printf("Reading group %v: %v", name, err)
}
g.dead = true
delGroupUnlocked(name)
return nil, err
}
if changed {
desc, err := GetDescription(name) desc, err := GetDescription(name)
if err != nil { if err != nil {
if !os.IsNotExist(err) { if !os.IsNotExist(err) {
...@@ -478,6 +470,7 @@ func matchUser(user ClientCredentials, users []ClientCredentials) (bool, bool) { ...@@ -478,6 +470,7 @@ func matchUser(user ClientCredentials, users []ClientCredentials) (bool, bool) {
} }
type description struct { type description struct {
fileName string `json:"-"`
loadTime time.Time `json:"-"` loadTime time.Time `json:"-"`
modTime time.Time `json:"-"` modTime time.Time `json:"-"`
fileSize int64 `json:"-"` fileSize int64 `json:"-"`
...@@ -485,27 +478,65 @@ type description struct { ...@@ -485,27 +478,65 @@ type description struct {
Redirect string `json:"redirect,omitempty"` Redirect string `json:"redirect,omitempty"`
Public bool `json:"public,omitempty"` Public bool `json:"public,omitempty"`
MaxClients int `json:"max-clients,omitempty"` MaxClients int `json:"max-clients,omitempty"`
MaxHistoryAge int `json:"max-history-age",omitempty` MaxHistoryAge int `json:"max-history-age,omitempty"`
AllowAnonymous bool `json:"allow-anonymous,omitempty"` AllowAnonymous bool `json:"allow-anonymous,omitempty"`
AllowRecording bool `json:"allow-recording,omitempty"` AllowRecording bool `json:"allow-recording,omitempty"`
AllowSubgroups bool `json:"allow-subgroups,omitempty"`
Op []ClientCredentials `json:"op,omitempty"` Op []ClientCredentials `json:"op,omitempty"`
Presenter []ClientCredentials `json:"presenter,omitempty"` Presenter []ClientCredentials `json:"presenter,omitempty"`
Other []ClientCredentials `json:"other,omitempty"` Other []ClientCredentials `json:"other,omitempty"`
} }
func descriptionChanged(name string, old *description) (bool, error) { func openDescriptionFile(name string) (*os.File, string, bool, error) {
fi, err := os.Stat(filepath.Join(Directory, name+".json")) isParent := false
if err != nil { for name != "" {
return false, err fileName := filepath.Join(
Directory, path.Clean("/"+name)+".json",
)
r, err := os.Open(fileName)
if !os.IsNotExist(err) {
return r, fileName, isParent, err
}
isParent = true
name, _ = path.Split(name)
name = strings.TrimRight(name, "/")
} }
if fi.Size() != old.fileSize || fi.ModTime() != old.modTime { return nil, "", false, os.ErrNotExist
return true, err }
func statDescriptionFile(name string) (os.FileInfo, string, bool, error) {
isParent := false
for name != "" {
fileName := filepath.Join(
Directory, path.Clean("/"+name)+".json",
)
fi, err := os.Stat(fileName)
if !os.IsNotExist(err) {
return fi, fileName, isParent, err
}
isParent = true
name, _ = path.Split(name)
name = strings.TrimRight(name, "/")
} }
return false, err return nil, "", false, os.ErrNotExist
}
// descriptionChanged returns true if a group's description may have
// changed since it was last read.
func descriptionChanged(name string, desc *description) bool {
fi, fileName, _, err := statDescriptionFile(name)
if err != nil || fileName != desc.fileName {
return true
}
if fi.Size() != desc.fileSize || fi.ModTime() != desc.modTime {
return true
}
return false
} }
func GetDescription(name string) (*description, error) { func GetDescription(name string) (*description, error) {
r, err := os.Open(filepath.Join(Directory, name+".json")) r, fileName, isParent, err := openDescriptionFile(name)
if err != nil { if err != nil {
return nil, err return nil, err
} }
...@@ -517,15 +548,25 @@ func GetDescription(name string) (*description, error) { ...@@ -517,15 +548,25 @@ func GetDescription(name string) (*description, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
desc.fileSize = fi.Size()
desc.modTime = fi.ModTime()
d := json.NewDecoder(r) d := json.NewDecoder(r)
err = d.Decode(&desc) err = d.Decode(&desc)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if isParent {
if !desc.AllowSubgroups {
return nil, os.ErrNotExist
}
desc.Public = false
desc.Description = ""
}
desc.fileName = fileName
desc.fileSize = fi.Size()
desc.modTime = fi.ModTime()
desc.loadTime = time.Now() desc.loadTime = time.Now()
return &desc, nil return &desc, nil
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment