@@ 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
}
@@ 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()