269 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
			
		
		
	
	
			269 lines
		
	
	
		
			5.9 KiB
		
	
	
	
		
			Go
		
	
	
	
// Package filemanager provides middleware for managing files in a directory
 | 
						|
// when directory path is requested instead of a specific file. Based on browse
 | 
						|
// middleware.
 | 
						|
package filemanager
 | 
						|
 | 
						|
import (
 | 
						|
	"fmt"
 | 
						|
	"io/ioutil"
 | 
						|
	"log"
 | 
						|
	"net/http"
 | 
						|
	"os"
 | 
						|
	"os/exec"
 | 
						|
	"path/filepath"
 | 
						|
	"regexp"
 | 
						|
	"strconv"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/hacdias/filemanager"
 | 
						|
	"github.com/mholt/caddy"
 | 
						|
	"github.com/mholt/caddy/caddyhttp/httpserver"
 | 
						|
)
 | 
						|
 | 
						|
func init() {
 | 
						|
	caddy.RegisterPlugin("filemanager", caddy.Plugin{
 | 
						|
		ServerType: "http",
 | 
						|
		Action:     setup,
 | 
						|
	})
 | 
						|
}
 | 
						|
 | 
						|
// FileManager is an http.Handler that can show a file listing when
 | 
						|
// directories in the given paths are specified.
 | 
						|
type FileManager struct {
 | 
						|
	Next    httpserver.Handler
 | 
						|
	Configs []*filemanager.FileManager
 | 
						|
}
 | 
						|
 | 
						|
// ServeHTTP determines if the request is for this plugin, and if all prerequisites are met.
 | 
						|
func (f FileManager) ServeHTTP(w http.ResponseWriter, r *http.Request) (int, error) {
 | 
						|
	for i := range f.Configs {
 | 
						|
		// Checks if this Path should be handled by File Manager.
 | 
						|
		if !httpserver.Path(r.URL.Path).Matches(f.Configs[i].BaseURL) {
 | 
						|
			continue
 | 
						|
		}
 | 
						|
 | 
						|
		return f.Configs[i].ServeHTTP(w, r)
 | 
						|
	}
 | 
						|
 | 
						|
	return f.Next.ServeHTTP(w, r)
 | 
						|
}
 | 
						|
 | 
						|
// setup configures a new FileManager middleware instance.
 | 
						|
func setup(c *caddy.Controller) error {
 | 
						|
	configs, err := parse(c)
 | 
						|
	if err != nil {
 | 
						|
		return err
 | 
						|
	}
 | 
						|
 | 
						|
	httpserver.GetConfig(c).AddMiddleware(func(next httpserver.Handler) httpserver.Handler {
 | 
						|
		return FileManager{Configs: configs, Next: next}
 | 
						|
	})
 | 
						|
 | 
						|
	return nil
 | 
						|
}
 | 
						|
 | 
						|
func parse(c *caddy.Controller) ([]*filemanager.FileManager, error) {
 | 
						|
	var (
 | 
						|
		configs []*filemanager.FileManager
 | 
						|
		err     error
 | 
						|
	)
 | 
						|
 | 
						|
	for c.Next() {
 | 
						|
		var (
 | 
						|
			m    = filemanager.New(".")
 | 
						|
			u    = m.User
 | 
						|
			name = ""
 | 
						|
		)
 | 
						|
 | 
						|
		caddyConf := httpserver.GetConfig(c)
 | 
						|
 | 
						|
		m.SetPrefixURL(strings.TrimSuffix(caddyConf.Addr.Path, "/"))
 | 
						|
		m.Commands = []string{"git", "svn", "hg"}
 | 
						|
		m.Rules = append(m.Rules, &filemanager.Rule{
 | 
						|
			Regex:  true,
 | 
						|
			Allow:  false,
 | 
						|
			Regexp: regexp.MustCompile("\\/\\..+"),
 | 
						|
		})
 | 
						|
 | 
						|
		// Get the baseURL
 | 
						|
		args := c.RemainingArgs()
 | 
						|
 | 
						|
		if len(args) > 0 {
 | 
						|
			m.SetBaseURL(args[0])
 | 
						|
			m.SetWebDavURL("/webdav")
 | 
						|
		}
 | 
						|
 | 
						|
		for c.NextBlock() {
 | 
						|
			switch c.Val() {
 | 
						|
			case "before_save":
 | 
						|
				if m.BeforeSave, err = makeCommand(c); err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
			case "after_save":
 | 
						|
				if m.AfterSave, err = makeCommand(c); err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
			case "webdav":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				m.SetWebDavURL(c.Val())
 | 
						|
			case "show":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				m.SetScope(c.Val(), name)
 | 
						|
			case "styles":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				var tplBytes []byte
 | 
						|
				tplBytes, err = ioutil.ReadFile(c.Val())
 | 
						|
				if err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
 | 
						|
				u.StyleSheet = string(tplBytes)
 | 
						|
			case "allow_new":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				u.AllowNew, err = strconv.ParseBool(c.Val())
 | 
						|
				if err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
			case "allow_edit":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				u.AllowEdit, err = strconv.ParseBool(c.Val())
 | 
						|
				if err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
			case "allow_commands":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				u.AllowCommands, err = strconv.ParseBool(c.Val())
 | 
						|
				if err != nil {
 | 
						|
					return configs, err
 | 
						|
				}
 | 
						|
			case "allow_command":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				u.Commands = append(u.Commands, c.Val())
 | 
						|
			case "block_command":
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				index := 0
 | 
						|
 | 
						|
				for i, val := range u.Commands {
 | 
						|
					if val == c.Val() {
 | 
						|
						index = i
 | 
						|
					}
 | 
						|
				}
 | 
						|
 | 
						|
				u.Commands = append(u.Commands[:index], u.Commands[index+1:]...)
 | 
						|
			case "allow", "allow_r", "block", "block_r":
 | 
						|
				ruleType := c.Val()
 | 
						|
 | 
						|
				if !c.NextArg() {
 | 
						|
					return configs, c.ArgErr()
 | 
						|
				}
 | 
						|
 | 
						|
				if c.Val() == "dotfiles" && !strings.HasSuffix(ruleType, "_r") {
 | 
						|
					ruleType += "_r"
 | 
						|
				}
 | 
						|
 | 
						|
				rule := &filemanager.Rule{
 | 
						|
					Allow: ruleType == "allow" || ruleType == "allow_r",
 | 
						|
					Regex: ruleType == "allow_r" || ruleType == "block_r",
 | 
						|
				}
 | 
						|
 | 
						|
				if rule.Regex && c.Val() == "dotfiles" {
 | 
						|
					rule.Regexp = regexp.MustCompile("\\/\\..+")
 | 
						|
				} else if rule.Regex {
 | 
						|
					rule.Regexp = regexp.MustCompile(c.Val())
 | 
						|
				} else {
 | 
						|
					rule.Path = c.Val()
 | 
						|
				}
 | 
						|
 | 
						|
				u.Rules = append(u.Rules, rule)
 | 
						|
			default:
 | 
						|
				// Is it a new user? Is it?
 | 
						|
				val := c.Val()
 | 
						|
 | 
						|
				// Checks if it's a new user!
 | 
						|
				if !strings.HasSuffix(val, ":") {
 | 
						|
					fmt.Println("Unknown option " + val)
 | 
						|
				}
 | 
						|
 | 
						|
				// Get the username, sets the current user, and initializes it
 | 
						|
				val = strings.TrimSuffix(val, ":")
 | 
						|
				m.NewUser(val)
 | 
						|
				name = val
 | 
						|
			}
 | 
						|
		}
 | 
						|
 | 
						|
		configs = append(configs, m)
 | 
						|
	}
 | 
						|
 | 
						|
	return configs, nil
 | 
						|
}
 | 
						|
 | 
						|
func makeCommand(c *caddy.Controller) (filemanager.Command, error) {
 | 
						|
	fn := func(r *http.Request, c *filemanager.FileManager, u *filemanager.User) error { return nil }
 | 
						|
 | 
						|
	args := c.RemainingArgs()
 | 
						|
	if len(args) == 0 {
 | 
						|
		return fn, c.ArgErr()
 | 
						|
	}
 | 
						|
 | 
						|
	nonblock := false
 | 
						|
	if len(args) > 1 && args[len(args)-1] == "&" {
 | 
						|
		// Run command in background; non-blocking
 | 
						|
		nonblock = true
 | 
						|
		args = args[:len(args)-1]
 | 
						|
	}
 | 
						|
 | 
						|
	command, args, err := caddy.SplitCommandAndArgs(strings.Join(args, " "))
 | 
						|
	if err != nil {
 | 
						|
		return fn, c.Err(err.Error())
 | 
						|
	}
 | 
						|
 | 
						|
	fn = func(r *http.Request, c *filemanager.FileManager, u *filemanager.User) error {
 | 
						|
		path := strings.Replace(r.URL.Path, c.WebDavURL, "", 1)
 | 
						|
		path = u.Scope() + "/" + path
 | 
						|
		path = filepath.Clean(path)
 | 
						|
 | 
						|
		for i := range args {
 | 
						|
			args[i] = strings.Replace(args[i], "{path}", path, -1)
 | 
						|
		}
 | 
						|
 | 
						|
		cmd := exec.Command(command, args...)
 | 
						|
		cmd.Stdin = os.Stdin
 | 
						|
		cmd.Stdout = os.Stdout
 | 
						|
		cmd.Stderr = os.Stderr
 | 
						|
 | 
						|
		if nonblock {
 | 
						|
			log.Printf("[INFO] Nonblocking Command:\"%s %s\"", command, strings.Join(args, " "))
 | 
						|
			return cmd.Start()
 | 
						|
		}
 | 
						|
 | 
						|
		log.Printf("[INFO] Blocking Command:\"%s %s\"", command, strings.Join(args, " "))
 | 
						|
		return cmd.Run()
 | 
						|
	}
 | 
						|
 | 
						|
	return fn, nil
 | 
						|
}
 |