From 29bd67cc2224d662ad8732395630ef43337beb76 Mon Sep 17 00:00:00 2001 From: Juliusz Chroboczek Date: Sun, 22 Nov 2020 19:54:54 +0100 Subject: [PATCH] Implement subgroups. --- README | 2 ++ group/group.go | 83 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 64 insertions(+), 21 deletions(-) diff --git a/README b/README index dac3019..2918dc2 100644 --- a/README +++ b/README @@ -119,6 +119,8 @@ fields, all of which are optional. kept (default 14400, i.e. 4 hours); - `allow-recording`: if true, then recording is allowed in this group; - `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 to the given URL; most other fields are ignored in this case. diff --git a/group/group.go b/group/group.go index b828978..f82c6ea 100644 --- a/group/group.go +++ b/group/group.go @@ -9,6 +9,7 @@ import ( "encoding/json" "log" "os" + "path" "path/filepath" "sort" "strings" @@ -198,16 +199,7 @@ func Add(name string, desc *description) (*Group, error) { } if g.dead || time.Since(g.description.loadTime) > 5*time.Second { - changed, err := 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 { + if descriptionChanged(name, g.description) { desc, err := GetDescription(name) if err != nil { if !os.IsNotExist(err) { @@ -478,6 +470,7 @@ func matchUser(user ClientCredentials, users []ClientCredentials) (bool, bool) { } type description struct { + fileName string `json:"-"` loadTime time.Time `json:"-"` modTime time.Time `json:"-"` fileSize int64 `json:"-"` @@ -485,27 +478,65 @@ type description struct { Redirect string `json:"redirect,omitempty"` Public bool `json:"public,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"` AllowRecording bool `json:"allow-recording,omitempty"` + AllowSubgroups bool `json:"allow-subgroups,omitempty"` Op []ClientCredentials `json:"op,omitempty"` Presenter []ClientCredentials `json:"presenter,omitempty"` Other []ClientCredentials `json:"other,omitempty"` } -func descriptionChanged(name string, old *description) (bool, error) { - fi, err := os.Stat(filepath.Join(Directory, name+".json")) - if err != nil { - return false, err +func openDescriptionFile(name string) (*os.File, string, bool, error) { + isParent := false + for name != "" { + 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 true, err + return nil, "", false, os.ErrNotExist +} + +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) { - r, err := os.Open(filepath.Join(Directory, name+".json")) + r, fileName, isParent, err := openDescriptionFile(name) if err != nil { return nil, err } @@ -517,15 +548,25 @@ func GetDescription(name string) (*description, error) { if err != nil { return nil, err } - desc.fileSize = fi.Size() - desc.modTime = fi.ModTime() d := json.NewDecoder(r) err = d.Decode(&desc) if err != nil { 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() + return &desc, nil }