package cgi import ( "bytes" "context" "fmt" "path/filepath" "strings" sr "tildegit.org/tjp/sliderule" "tildegit.org/tjp/sliderule/gemini" "tildegit.org/tjp/sliderule/logging" ) // GeminiCGIDirectory runs any executable files relative to a root directory on the file system. // // It will also find and run any executables _part way_ through the path, so for example // 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 GeminiCGIDirectory(fsroot, urlroot, cmd string) sr.Handler { fsroot = strings.TrimRight(fsroot, "/") return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response { if !strings.HasPrefix(request.Path, urlroot) { return nil } execpath, pathinfo, err := ResolveCGI(request.Path[len(urlroot):], fsroot) if err != nil { return gemini.Failure(err) } if execpath == "" { return nil } workdir := filepath.Dir(execpath) if cmd != "" { execpath = cmd } stderr := &bytes.Buffer{} stdout, exitCode, err := RunCGI(ctx, request, execpath, pathinfo, workdir, stderr) if err != nil { return gemini.Failure(err) } if exitCode != 0 { ctx.Value("warnlog").(logging.Logger).Log( "msg", "cgi exited with non-zero exit code", "code", exitCode, "stderr", stderr.String(), ) return gemini.CGIError(fmt.Sprintf("CGI process exited with status %d", exitCode)) } response, err := gemini.ParseResponse(stdout) if err != nil { return gemini.Failure(err) } return response }) }