some path validation and some stronger typing
2 files changed, 44 insertions(+), 116 deletions(-)

M resources.go
M server.go
M resources.go +14 -11
@@ 19,7 19,6 @@ const (
 
 var (
     InvalidPath         = fmt.Errorf("Invalid path")
-    ResourceNotFound    = fmt.Errorf("Required resource was not found")
 )
 
 type Request struct {

          
@@ 39,19 38,23 @@ type Message struct {
     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
     }

          
M server.go +30 -105
@@ 5,7 5,7 @@ import _"os"
 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 @@ func NewGoquiturHandler(watchdir string)
     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 @@ func (h *GoquiturHandler) handleEvents(e
         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 @@ func (h *GoquiturHandler) handleEvents(e
                         // 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) handleEvents(e
 
 ////////////////////////////////////////////////////////////////////////////////
 /*
-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()