Issue #32: working live with WebSockets! Needs to be reviewed.
This commit is contained in:
		
							parent
							
								
									12c76b7a54
								
							
						
					
					
						commit
						44065cfaf9
					
				| 
						 | 
				
			
			@ -473,8 +473,8 @@ header {
 | 
			
		|||
    z-index: 999;
 | 
			
		||||
    padding: 1.7em 0;
 | 
			
		||||
    background-color: #2196f3;
 | 
			
		||||
    border-bottom: 1px solid rgba(0,0,0,0.075);
 | 
			
		||||
    box-shadow: 0 0 5px rgba(0,0,0,0.1);
 | 
			
		||||
    border-bottom: 1px solid rgba(0, 0, 0, 0.075);
 | 
			
		||||
    box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
header h1 {
 | 
			
		||||
| 
						 | 
				
			
			@ -548,7 +548,7 @@ header p i {
 | 
			
		|||
    min-width: 20em;
 | 
			
		||||
    border: 0;
 | 
			
		||||
    outline: 0;
 | 
			
		||||
    color: #fff;
 | 
			
		||||
    color: rgba(255, 255, 255, 0.72);
 | 
			
		||||
    background-color: transparent;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -574,14 +574,22 @@ header p i {
 | 
			
		|||
    transition: .1s ease all;
 | 
			
		||||
    visibility: hidden;
 | 
			
		||||
    opacity: 0;
 | 
			
		||||
    overflow-x: hidden;
 | 
			
		||||
    overflow-y: auto;
 | 
			
		||||
    max-height: 50vh;
 | 
			
		||||
    white-space: pre-wrap;
 | 
			
		||||
    white-space: -moz-pre-wrap;
 | 
			
		||||
    white-space: -pre-wrap;
 | 
			
		||||
    white-space: -o-pre-wrap;
 | 
			
		||||
    word-wrap: break-word;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#search.active div i,
 | 
			
		||||
#sidebar #search.active div i {
 | 
			
		||||
    color: #ccc;
 | 
			
		||||
    display: block;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    margin: 0 auto;
 | 
			
		||||
    display: table;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#search::-webkit-input-placeholder {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -503,29 +503,18 @@ var searchEvent = function(event) {
 | 
			
		|||
    if (event.keyCode == 13) {
 | 
			
		||||
        box.innerHTML = '<i class="material-icons spin">autorenew</i>';
 | 
			
		||||
 | 
			
		||||
        let request = new XMLHttpRequest();
 | 
			
		||||
        request.open('POST', window.location);
 | 
			
		||||
        request.setRequestHeader('Command', value);
 | 
			
		||||
        request.setRequestHeader('Token', token);
 | 
			
		||||
        request.send();
 | 
			
		||||
        request.onreadystatechange = function() {
 | 
			
		||||
            if (request.readyState == 4) {
 | 
			
		||||
                if (request.status == 501) {
 | 
			
		||||
                    box.innerHTML = "Command not implemented."
 | 
			
		||||
                }
 | 
			
		||||
        var conn = new WebSocket('ws://' + window.location.host + window.location.pathname + '?command=true');
 | 
			
		||||
        conn.onopen = function() {
 | 
			
		||||
            conn.send(value);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
                if (request.status == 500) {
 | 
			
		||||
                    box.innerHTML = "Something went wrong."
 | 
			
		||||
                }
 | 
			
		||||
        conn.onmessage = function(event) {
 | 
			
		||||
            box.innerHTML = event.data
 | 
			
		||||
            box.scrollTop = box.scrollHeight;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
                if (request.status == 200) {
 | 
			
		||||
                    let text = request.responseText;
 | 
			
		||||
                    text = text.substring(1, text.length - 1);
 | 
			
		||||
                    text = text.replace('\\n', "\n");
 | 
			
		||||
                    box.innerHTML = text;
 | 
			
		||||
                    reloadListing();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        conn.onclose = function(event) {
 | 
			
		||||
            reloadListing();
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -554,13 +543,28 @@ document.addEventListener('listing', event => {
 | 
			
		|||
    });
 | 
			
		||||
 | 
			
		||||
    if (user.AllowCommands) {
 | 
			
		||||
        let hover = false, focus = false;
 | 
			
		||||
 | 
			
		||||
        document.querySelector('#search input').addEventListener('focus', event => {
 | 
			
		||||
            focus = true;
 | 
			
		||||
            document.getElementById('search').classList.add('active');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        document.querySelector('#search div').addEventListener('mouseover', event => {
 | 
			
		||||
            hover = true;
 | 
			
		||||
            document.getElementById('search').classList.add('active');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        document.querySelector('#search input').addEventListener('blur', event => {
 | 
			
		||||
            focus = false;
 | 
			
		||||
            if (hover) return;
 | 
			
		||||
            document.getElementById('search').classList.remove('active');
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        document.querySelector('#search').addEventListener('mouseleave', event => {
 | 
			
		||||
            hover = false;
 | 
			
		||||
            if (focus) return;
 | 
			
		||||
            document.getElementById('search').classList.remove('active');
 | 
			
		||||
            document.querySelector('#search input').value = '';
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        document.querySelector('#search div').innerHTML = "Write one of yours suported commands: " + user.Commands.join(", ") + ".";
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,7 +42,7 @@ func GetInfo(url *url.URL, c *config.Config, u *config.User) (*Info, int, error)
 | 
			
		|||
 | 
			
		||||
	i.FileInfo, err = os.Stat(i.Path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return i, errors.ErrorToHTTPCode(err, true), err
 | 
			
		||||
		return i, errors.ErrorToHTTPCode(err, false), err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return i, 0, nil
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,6 +67,16 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
 | 
			
		|||
			user = c.User
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.URL.Query().Get("command") != "" {
 | 
			
		||||
			return handlers.Command(w, r, c, user)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// TODO: This anti CSCF measure is not being applied to requests
 | 
			
		||||
		// to the WebDav URL namespace. Anyone has ideas?
 | 
			
		||||
		//	if !c.CheckToken(r) {
 | 
			
		||||
		//	return http.StatusForbidden, nil
 | 
			
		||||
		//	}
 | 
			
		||||
 | 
			
		||||
		// Checks if the request URL is for the WebDav server
 | 
			
		||||
		if strings.HasPrefix(r.URL.Path, c.WebDavURL) {
 | 
			
		||||
			//	if !c.CheckToken(r) {
 | 
			
		||||
| 
						 | 
				
			
			@ -126,23 +136,6 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
 | 
			
		|||
			co, err := r.Cookie("token")
 | 
			
		||||
			fmt.Println(co.Value) */
 | 
			
		||||
 | 
			
		||||
			/* Name  string
 | 
			
		||||
			   Value string
 | 
			
		||||
 | 
			
		||||
			   Path       string    // optional
 | 
			
		||||
			   Domain     string    // optional
 | 
			
		||||
			   Expires    time.Time // optional
 | 
			
		||||
			   RawExpires string    // for reading cookies only
 | 
			
		||||
 | 
			
		||||
			   // MaxAge=0 means no 'Max-Age' attribute specified.
 | 
			
		||||
			   // MaxAge<0 means delete cookie now, equivalently 'Max-Age: 0'
 | 
			
		||||
			   // MaxAge>0 means Max-Age attribute present and given in seconds
 | 
			
		||||
			   MaxAge   int
 | 
			
		||||
			   Secure   bool
 | 
			
		||||
			   HttpOnly bool
 | 
			
		||||
			   Raw      string
 | 
			
		||||
			   Unparsed []string // Raw text of unparsed attribute-value pairs*/
 | 
			
		||||
 | 
			
		||||
			// Gets the information of the directory/file
 | 
			
		||||
			fi, code, err = file.GetInfo(r.URL, c, user)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
| 
						 | 
				
			
			@ -178,23 +171,6 @@ func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, err
 | 
			
		|||
			return code, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if r.Method == http.MethodPost {
 | 
			
		||||
			// TODO: This anti CSCF measure is not being applied to requests
 | 
			
		||||
			// to the WebDav URL namespace. Anyone has ideas?
 | 
			
		||||
			//	if !c.CheckToken(r) {
 | 
			
		||||
			//	return http.StatusForbidden, nil
 | 
			
		||||
			//	}
 | 
			
		||||
 | 
			
		||||
			// VCS commands.
 | 
			
		||||
			if r.Header.Get("Command") != "" {
 | 
			
		||||
				if !user.AllowCommands {
 | 
			
		||||
					return http.StatusUnauthorized, nil
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				return handlers.Command(w, r, c, user)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return http.StatusNotImplemented, nil
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,18 +1,122 @@
 | 
			
		|||
package handlers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/gorilla/websocket"
 | 
			
		||||
	"github.com/hacdias/caddy-filemanager/config"
 | 
			
		||||
	"github.com/hacdias/caddy-filemanager/page"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var upgrader = websocket.Upgrader{
 | 
			
		||||
	ReadBufferSize:  1024,
 | 
			
		||||
	WriteBufferSize: 1024,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 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) {
 | 
			
		||||
	command := strings.Split(r.Header.Get("command"), " ")
 | 
			
		||||
	conn, err := upgrader.Upgrade(w, r, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Println(err)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	defer conn.Close()
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		_, message, err := conn.ReadMessage()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Println("read:", err)
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		command := strings.Split(string(message), " ")
 | 
			
		||||
 | 
			
		||||
		if len(command) == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		// Check if the command is allowed
 | 
			
		||||
		mayContinue := false
 | 
			
		||||
 | 
			
		||||
		for _, cmd := range u.Commands {
 | 
			
		||||
			if cmd == command[0] {
 | 
			
		||||
				mayContinue = true
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !mayContinue {
 | 
			
		||||
			err = conn.WriteMessage(websocket.BinaryMessage, []byte("FORBIDDEN"))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("write:", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		// Check if the program is talled is installed on the computer
 | 
			
		||||
		if _, err = exec.LookPath(command[0]); err != nil {
 | 
			
		||||
			err = conn.WriteMessage(websocket.BinaryMessage, []byte("Command not implemented."))
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("write:", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			return http.StatusNotImplemented, nil
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		path := strings.Replace(r.URL.Path, c.BaseURL, c.Scope, 1)
 | 
			
		||||
		path = filepath.Clean(path)
 | 
			
		||||
 | 
			
		||||
		buff := new(bytes.Buffer)
 | 
			
		||||
 | 
			
		||||
		cmd := exec.Command(command[0], command[1:len(command)]...)
 | 
			
		||||
		cmd.Dir = path
 | 
			
		||||
		cmd.Stderr = buff
 | 
			
		||||
		cmd.Stdout = buff
 | 
			
		||||
		err = cmd.Start()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return http.StatusInternalServerError, err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		done := false
 | 
			
		||||
		go func() {
 | 
			
		||||
			err = cmd.Wait()
 | 
			
		||||
			done = true
 | 
			
		||||
		}()
 | 
			
		||||
 | 
			
		||||
		for !done {
 | 
			
		||||
			by := buff.Bytes()
 | 
			
		||||
			if len(by) > 0 {
 | 
			
		||||
				err = conn.WriteMessage(websocket.TextMessage, by)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					fmt.Println("write:", err)
 | 
			
		||||
					break
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			time.Sleep(100 * time.Millisecond)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		by := buff.Bytes()
 | 
			
		||||
		if len(by) > 0 {
 | 
			
		||||
			err = conn.WriteMessage(websocket.TextMessage, by)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Println("write:", err)
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		time.Sleep(100 * time.Millisecond)
 | 
			
		||||
 | 
			
		||||
		break
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* command := strings.Split(r.Header.Get("command"), " ")
 | 
			
		||||
 | 
			
		||||
	// Check if the command is allowed
 | 
			
		||||
	mayContinue := false
 | 
			
		||||
| 
						 | 
				
			
			@ -37,12 +141,44 @@ func Command(w http.ResponseWriter, r *http.Request, c *config.Config, u *config
 | 
			
		|||
 | 
			
		||||
	cmd := exec.Command(command[0], command[1:len(command)]...)
 | 
			
		||||
	cmd.Dir = path
 | 
			
		||||
	output, err := cmd.CombinedOutput()
 | 
			
		||||
	cmd.Stderr = w
 | 
			
		||||
	cmd.Stdout = w
 | 
			
		||||
	cmd.Start()
 | 
			
		||||
 | 
			
		||||
	/*cmd.Stderr = b
 | 
			
		||||
	cmd.Stdout = b
 | 
			
		||||
 | 
			
		||||
	// Starts the comamnd
 | 
			
		||||
	err := cmd.Start()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return http.StatusInternalServerError, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := &page.Page{Info: &page.Info{Data: string(output)}}
 | 
			
		||||
	return p.PrintAsJSON(w)
 | 
			
		||||
	done := false
 | 
			
		||||
	go func() {
 | 
			
		||||
		err = cmd.Wait()
 | 
			
		||||
		done = true
 | 
			
		||||
	}()
 | 
			
		||||
 | 
			
		||||
	for !done {
 | 
			
		||||
		by := b.Bytes()
 | 
			
		||||
		if len(by) > 0 {
 | 
			
		||||
			fmt.Println(string(by))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//w.Write(by)
 | 
			
		||||
 | 
			
		||||
	}*/
 | 
			
		||||
 | 
			
		||||
	//out, err := cmd.CombinedOutput()
 | 
			
		||||
	//fmt.Println(string(out))
 | 
			
		||||
 | 
			
		||||
	//if err != nil {
 | 
			
		||||
	//	return http.StatusInternalServerError, err
 | 
			
		||||
	//}
 | 
			
		||||
 | 
			
		||||
	/* cmd.Wait()
 | 
			
		||||
 | 
			
		||||
	//p := &page.Page{Info: &page.Info{Data: string(output)}} */
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue