diff --git a/README.md b/README.md index 96301f10..2abc2fdb 100644 --- a/README.md +++ b/README.md @@ -141,6 +141,28 @@ filemanager /admin { } ``` +## About Search + +FileManager allows you to search through your files and it has some options. By default, your search will be something like this: + +``` +this are keywords +``` + +If you search for that it will look at every file that contains "this", "are" and "keywords" on their name. If you want to search for an exact term, you should surround your search by double quotes: + +``` +"this is the name" +``` + +That will search for any file that contains "this is the name" on its name. It won't search for each separated term this time. + +By default, every search will be case sensitive. Although, you can make a case insensitive search by adding `case:insensitive` to the search terms, like this: + +``` +this are keywords case:insensitive +``` + ## Developers If you want to build Caddy from source with this plugin, you should take the following steps: diff --git a/handlers/search.go b/handlers/search.go index 22f3ca82..978f18d1 100644 --- a/handlers/search.go +++ b/handlers/search.go @@ -10,6 +10,39 @@ import ( "github.com/hacdias/caddy-filemanager/config" ) +type searchOptions struct { + CaseInsensitive bool + Terms []string +} + +func parseSearch(value string) *searchOptions { + opts := &searchOptions{ + CaseInsensitive: strings.Contains(value, "case:insensitive"), + } + + // removes the options from the value + value = strings.Replace(value, "case:insensitive", "", -1) + value = strings.Replace(value, "case:sensitive", "", -1) + value = strings.TrimSpace(value) + + if opts.CaseInsensitive { + value = strings.ToLower(value) + } + + // if the value starts with " and finishes what that character, we will + // only search for that term + if value[0] == '"' && value[len(value)-1] == '"' { + unique := strings.TrimPrefix(value, "\"") + unique = strings.TrimSuffix(unique, "\"") + + opts.Terms = []string{unique} + return opts + } + + opts.Terms = strings.Split(value, " ") + return opts +} + // Search ... func Search(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) { // Upgrades the connection to a websocket and checks for errors. @@ -20,12 +53,11 @@ func Search(w http.ResponseWriter, r *http.Request, c *config.Config, u *config. defer conn.Close() var ( - search string + value string + search *searchOptions message []byte ) - caseInsensitive := (r.URL.Query().Get("insensitive") == "true") - // Starts an infinite loop until a valid command is captured. for { _, message, err = conn.ReadMessage() @@ -34,15 +66,12 @@ func Search(w http.ResponseWriter, r *http.Request, c *config.Config, u *config. } if len(message) != 0 { - search = string(message) + value = string(message) break } } - if caseInsensitive { - search = strings.ToLower(search) - } - + search = parseSearch(value) scope := strings.Replace(r.URL.Path, c.BaseURL, "", 1) scope = strings.TrimPrefix(scope, "/") scope = "/" + scope @@ -51,22 +80,25 @@ func Search(w http.ResponseWriter, r *http.Request, c *config.Config, u *config. scope = filepath.Clean(scope) err = filepath.Walk(scope, func(path string, f os.FileInfo, err error) error { - if caseInsensitive { + if search.CaseInsensitive { path = strings.ToLower(path) } - if strings.Contains(path, search) { - if !u.Allowed(path) { - return nil - } + path = strings.Replace(path, "\\", "/", -1) - path = strings.TrimPrefix(path, scope) - path = strings.Replace(path, "\\", "/", -1) - path = strings.TrimPrefix(path, "/") + for _, term := range search.Terms { + if strings.Contains(path, term) { + if !u.Allowed(path) { + return nil + } - err = conn.WriteMessage(websocket.TextMessage, []byte(path)) - if err != nil { - return err + path = strings.TrimPrefix(path, scope) + path = strings.TrimPrefix(path, "/") + + err = conn.WriteMessage(websocket.TextMessage, []byte(path)) + if err != nil { + return err + } } }