# HG changeset patch # User sqwishy # Date 1385539935 28800 # Wed Nov 27 00:12:15 2013 -0800 # Node ID 112cddbe7432e37af6c458090163fadd65e39df7 # Parent 89a54ca22f10f4108af6c52479d1be2cef5bcfe5 some path validation and some stronger typing diff --git a/resources.go b/resources.go --- a/resources.go +++ b/resources.go @@ -19,7 +19,6 @@ var ( InvalidPath = fmt.Errorf("Invalid path") - ResourceNotFound = fmt.Errorf("Required resource was not found") ) type Request struct { @@ -39,19 +38,23 @@ Payload interface{} `json:",omitempty"` } +/* Not quite a URL, it's the part of the URL that we handle + * For example: in https://example.com/goquitur/foo/bar, /foo/bar would be + * the WebPath + */ +type WebPath string +// A location on the filesystem +type FSPath string +// A location on the filesystem that has been validated, see ValidateFSPath +type ValidFSPath string + type Location struct { - WebPath string - FSPath string + WebPath WebPath + FSPath FSPath } -type OpenResource struct { - *Location - *os.File - os.FileInfo -} - -func getPayload(path string) (interface{}, error) { - f, err := os.Open(path) +func getPayload(path ValidFSPath) (interface{}, error) { + f, err := os.Open(string(path)) if err != nil { return nil, err } diff --git a/server.go b/server.go --- a/server.go +++ b/server.go @@ -5,7 +5,7 @@ import "log" import "sync" import _"fmt" -import _"strings" +import "strings" import "path/filepath" import "encoding/json" import "code.google.com/p/go.net/websocket" @@ -57,18 +57,27 @@ return h, nil } -func (h *GoquiturHandler) pathToLocation(path string) (*Location, error) { - webpath, err := filepath.Rel(h.watchdir, path) +// todo, maybe use a separate object for this for modularity or something +func (h *GoquiturHandler) ValidatePath(path FSPath) (ValidFSPath, error) { + var invalid ValidFSPath + if strings.HasPrefix(string(path), h.watchdir) == false { + return invalid, InvalidPath + } + return ValidFSPath(path), nil +} + +func (h *GoquiturHandler) locationFromPath(path FSPath) (*Location, error) { + webpath, err := filepath.Rel(h.watchdir, string(path)) if err != nil { return nil, err } - return &Location{webpath, path}, nil + return &Location{WebPath(webpath), path}, nil } -func (h *GoquiturHandler) Subscribers(path string) []*websocket.Conn { +func (h *GoquiturHandler) Subscribers(path WebPath) []*websocket.Conn { h.maplock.Lock() defer h.maplock.Unlock() - if v, ok := h.urlmap[path]; ok == true { + if v, ok := h.urlmap[string(path)]; ok == true { return v } return make([]*websocket.Conn, 0) @@ -80,18 +89,21 @@ case ev := <-evchan: log.Println("Got fs event!", ev) - loc, err := h.pathToLocation(ev.Path) + fspath := FSPath(ev.Path) + validpath, err := h.ValidatePath(fspath) + if err != nil { + log.Println("Error validating event:", err) + continue + } + loc, err := h.locationFromPath(fspath) if err != nil { log.Println("Error handling fs event:", err) continue } - log.Println("event at", loc) switch ev.Type { case EventCreate: - fallthrough case EventModify: - fallthrough case EventDelete: var data []byte for _, sub := range h.Subscribers(loc.WebPath) { @@ -100,14 +112,17 @@ // handle this event at all, so we break and move on // todo, set msg to some error thing and push that to // clients - payload, err := getPayload(loc.FSPath) - if err != nil { - log.Println("Error getting message payload:", err) - break + var payload interface{} + if ev.Type != EventDelete { + payload, err = getPayload(validpath) + if err != nil { + log.Println("Error getting message payload:", err) + break + } } msg := &Message{ Event: ev.Type, - Path: loc.WebPath, + Path: string(loc.WebPath), Payload: payload, } data, err = json.Marshal(msg) @@ -136,96 +151,6 @@ //////////////////////////////////////////////////////////////////////////////// /* -func (h *GoquiturHandler) resAdded(res *Resource) error { - ores, err := h.openResource(res) - if err != nil { - return err - } - payload, err := ores.Payload() - if err != nil { - return err - } - h.notifyConns(res.WebPath, &State{ - Event: EventCreate, - Path: res.WebPath, - Payload: payload, - }) - if res.WebPath != "/" { - parent, err := h.resFromWebPath(filepath.Dir(res.WebPath)) - if err != nil { - return err - } - return h.resModified(parent) - } - return nil -} - -func (h *GoquiturHandler) resModified(res *Resource) error { - ores, err := h.openResource(res) - if err != nil { - return err - } - payload, err := ores.Payload() - if err != nil { - return err - } - h.notifyConns(res.WebPath, &State{ - Event: EventModify, - Path: res.WebPath, - Payload: payload, - }) - return nil -} - -func (h *GoquiturHandler) resRemoved(res *Resource) error { - h.notifyConns(res.WebPath, &State{ - Event: EventDelete, - Path: res.WebPath, - }) - if res.WebPath != "/" { - parent, err := h.resFromWebPath(filepath.Dir(res.WebPath)) - if err != nil { - return err - } - return h.resModified(parent) - } - return nil -} - -//////////////////////////////////////////////////////////////////////////////// -func (h *GoquiturHandler) resFromWebPath(path string) (*Resource, error) { - if path == "" { - return nil, fmt.Errorf("Path must not be empty") - } - fspath, err := filepath.Abs(filepath.Join(h.watchdir, path)) - if err != nil { - return nil, err - } - //fspath, err = filepath.EvalSymlinks(fspath) - //if err != nil { - // return nil, err - //} - return &Resource{path, fspath}, nil -} - -func (h *GoquiturHandler) openResource(r *Resource) (*OpenResource, error) { - file, err := os.Open(r.FSPath) - if err != nil { - return nil,err - } - fi, err := file.Stat() - if err != nil { - return nil, err - } - return &OpenResource{r, file, fi}, nil -} - -func (h *GoquiturHandler) validateResource(r *Resource) error { - if strings.HasPrefix(r.FSPath, h.watchdir) == false { - return fmt.Errorf("Path does not appear to be inside a valid directory") - } - return nil -} func (h *GoquiturHandler) serveWS(ws *websocket.Conn) { defer ws.Close()