filebrowser/handlers/command.go

137 lines
3.0 KiB
Go
Raw Normal View History

2016-10-22 10:47:49 +00:00
package handlers
import (
"bytes"
2016-10-22 10:47:49 +00:00
"net/http"
"os/exec"
"path/filepath"
"strings"
"time"
2016-10-22 10:47:49 +00:00
"github.com/gorilla/websocket"
2016-10-22 10:47:49 +00:00
"github.com/hacdias/caddy-filemanager/config"
)
var upgrader = websocket.Upgrader{
ReadBufferSize: 1024,
WriteBufferSize: 1024,
}
2016-10-31 21:25:14 +00:00
var (
cmdNotImplemented = []byte("Command not implemented.")
cmdNotAllowed = []byte("Command not allowed.")
)
2016-10-22 10:47:49 +00:00
// Command handles the requests for VCS related commands: git, svn and mercurial
func Command(w http.ResponseWriter, r *http.Request, c *config.Config, u *config.User) (int, error) {
2016-10-31 21:25:14 +00:00
// Upgrades the connection to a websocket and checks for errors.
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil {
2016-10-31 21:25:14 +00:00
return 0, err
}
defer conn.Close()
2016-10-31 21:25:14 +00:00
var (
message []byte
command []string
)
2016-10-31 21:25:14 +00:00
// Starts an infinite loop until a valid command is captured.
for {
_, message, err = conn.ReadMessage()
if err != nil {
return http.StatusInternalServerError, err
}
2016-10-31 21:25:14 +00:00
command = strings.Split(string(message), " ")
if len(command) != 0 {
break
}
}
2016-10-22 10:47:49 +00:00
// Check if the command is allowed
2016-10-31 21:25:14 +00:00
allowed := false
2016-10-22 10:47:49 +00:00
for _, cmd := range u.Commands {
if cmd == command[0] {
2016-10-31 21:25:14 +00:00
allowed = true
2016-10-22 10:47:49 +00:00
}
}
2016-10-31 21:25:14 +00:00
if !allowed {
err = conn.WriteMessage(websocket.BinaryMessage, cmdNotAllowed)
if err != nil {
return http.StatusInternalServerError, err
}
return 0, nil
2016-10-22 10:47:49 +00:00
}
2016-10-31 21:25:14 +00:00
// Check if the program is talled is installed on the computer.
if _, err = exec.LookPath(command[0]); err != nil {
err = conn.WriteMessage(websocket.BinaryMessage, cmdNotImplemented)
if err != nil {
return http.StatusInternalServerError, err
}
2016-10-22 10:47:49 +00:00
return http.StatusNotImplemented, nil
}
2016-10-31 21:25:14 +00:00
// Gets the path and initializes a buffer.
2016-10-22 10:47:49 +00:00
path := strings.Replace(r.URL.Path, c.BaseURL, c.Scope, 1)
path = filepath.Clean(path)
2016-10-31 21:25:14 +00:00
buff := new(bytes.Buffer)
2016-10-22 10:47:49 +00:00
2016-10-31 21:25:14 +00:00
// Sets up the command executation.
cmd := exec.Command(command[0], command[1:]...)
2016-10-22 10:47:49 +00:00
cmd.Dir = path
2016-10-31 21:25:14 +00:00
cmd.Stderr = buff
cmd.Stdout = buff
2016-10-22 10:47:49 +00:00
2016-10-31 21:25:14 +00:00
// Starts the command and checks for errors.
err = cmd.Start()
2016-10-22 10:47:49 +00:00
if err != nil {
return http.StatusInternalServerError, err
}
2016-10-31 21:25:14 +00:00
// Set a 'done' variable to check whetever the command has already finished
// running or not. This verification is done using a goroutine that uses the
// method .Wait() from the command.
done := false
go func() {
err = cmd.Wait()
done = true
}()
2016-10-31 21:25:14 +00:00
// Function to print the current information on the buffer to the connection.
print := func() error {
by := buff.Bytes()
if len(by) > 0 {
2016-10-31 21:25:14 +00:00
err = conn.WriteMessage(websocket.TextMessage, by)
if err != nil {
return err
}
}
2016-10-31 21:25:14 +00:00
return nil
}
2016-10-31 21:25:14 +00:00
// While the command hasn't finished running, continue sending the output
// to the client in intervals of 100 milliseconds.
for !done {
if err = print(); err != nil {
return http.StatusInternalServerError, err
}
2016-10-31 21:25:14 +00:00
time.Sleep(100 * time.Millisecond)
}
2016-10-31 21:25:14 +00:00
// After the command is done executing, send the output one more time to the
// browser to make sure it gets the latest information.
if err = print(); err != nil {
return http.StatusInternalServerError, err
}
return 0, nil
2016-10-22 10:47:49 +00:00
}