diff options
Diffstat (limited to 'contrib/cgi/gopher.go')
| -rw-r--r-- | contrib/cgi/gopher.go | 139 | 
1 files changed, 130 insertions, 9 deletions
diff --git a/contrib/cgi/gopher.go b/contrib/cgi/gopher.go index 290adfa..98a3c75 100644 --- a/contrib/cgi/gopher.go +++ b/contrib/cgi/gopher.go @@ -3,10 +3,14 @@ package cgi  import (  	"context"  	"fmt" +	"os" +	"path" +	"path/filepath"  	"strings"  	sr "tildegit.org/tjp/sliderule"  	"tildegit.org/tjp/sliderule/gopher" +	"tildegit.org/tjp/sliderule/gopher/gophermap"  )  // GopherCGIDirectory runs any executable files relative to a root directory on the file system. @@ -15,31 +19,148 @@ import (  // a request for /foo/bar/baz can also run an executable found at /foo or  /foo/bar. In  // such a case the PATH_INFO environment variable will include the remaining portion of  // the URI path. -func GopherCGIDirectory(pathRoot, fsRoot string) sr.Handler { +func GopherCGIDirectory(pathRoot, fsRoot string, settings *gophermap.FileSystemSettings) sr.Handler { +	if settings == nil { +		settings = &gophermap.FileSystemSettings{} +	} + +	if !settings.Exec { +		return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { return nil }) +	} +  	fsRoot = strings.TrimRight(fsRoot, "/")  	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {  		if !strings.HasPrefix(request.Path, pathRoot) {  			return nil  		} -		filepath, pathinfo, err := ResolveCGI(request.Path[len(pathRoot):], fsRoot) +		fullpath, pathinfo, err := resolveGopherCGI(fsRoot, request)  		if err != nil {  			return gopher.Error(err).Response()  		} -		if filepath == "" { +		if fullpath == "" { +			return nil +		} + +		return runGopherCGI(ctx, request, fullpath, pathinfo, *settings) +	}) +} + +// ExecGopherMaps runs any gophermaps +func ExecGopherMaps(pathRoot, fsRoot string, settings *gophermap.FileSystemSettings) sr.Handler { +	if settings == nil { +		settings = &gophermap.FileSystemSettings{} +	} + +	if !settings.Exec { +		return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { return nil }) +	} + +	fsRoot = strings.TrimRight(fsRoot, "/") +	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { +		if !strings.HasPrefix(request.Path, pathRoot) {  			return nil  		} -		stdout, exitCode, err := RunCGI(ctx, request, filepath, pathinfo) +		fullpath := filepath.Join(fsRoot, strings.Trim(request.Path, "/")) +		info, err := os.Stat(fullpath) +		if isNotExistError(err) { +			return nil +		}  		if err != nil {  			return gopher.Error(err).Response()  		} -		if exitCode != 0 { -			return gopher.Error( -				fmt.Errorf("CGI process exited with status %d", exitCode), -			).Response() + +		if info.IsDir() { +			for _, fname := range settings.DirMaps { +				fpath := filepath.Join(fullpath, fname) +				finfo, err := os.Stat(fpath) +				if isNotExistError(err) { +					continue +				} +				if err != nil { +					return gopher.Error(err).Response() +				} + +				m := finfo.Mode() +				if m.IsDir() { +					continue +				} +				if !m.IsRegular() || m&5 != 5 { +					continue +				} +				return runGopherCGI(ctx, request, fpath, "/", *settings) +			} + +			return nil +		} + +		m := info.Mode() +		if !m.IsRegular() || m&5 != 5 { +			return nil  		} -		return gopher.File(0, stdout) +		return runGopherCGI(ctx, request, fullpath, "/", *settings)  	})  } + +func runGopherCGI( +	ctx context.Context, +	request *sr.Request, +	fullpath string, +	pathinfo string, +	settings gophermap.FileSystemSettings, +) *sr.Response { +	stdout, exitCode, err := RunCGI(ctx, request, fullpath, pathinfo) +	if err != nil { +		return gopher.Error(err).Response() +	} +	if exitCode != 0 { +		return gopher.Error( +			fmt.Errorf("CGI process exited with status %d", exitCode), +		).Response() +	} + +	if settings.ParseExtended { +		edoc, err := gophermap.ParseExtended(stdout, request.URL) +		if err != nil { +			return gopher.Error(err).Response() +		} + +		doc, _, err := edoc.Compatible(filepath.Dir(fullpath), settings) +		return doc.Response() +	} + +	return gopher.File(gopher.MenuType, stdout) +} + +func resolveGopherCGI(fsRoot string, request *sr.Request) (string, string, error) { +	reqPath := strings.TrimLeft(request.Path, "/") + +	segments := append([]string{""}, strings.Split(reqPath, "/")...) +	fullpath := fsRoot +	for i, segment := range segments { +		fullpath = filepath.Join(fullpath, segment) + +		info, err := os.Stat(fullpath) +		if isNotExistError(err) { +			return "", "", nil +		} +		if err != nil { +			return "", "", err +		} + +		if !info.IsDir() { +			if info.Mode()&5 == 5 { +				pathinfo := "/" +				if len(segments) > i+1 { +					pathinfo = path.Join(segments[i:]...) +				} +				return fullpath, pathinfo, nil +			} +			break +		} +	} + +	return "", "", nil +}  | 
