summaryrefslogtreecommitdiff
path: root/finger.go
blob: dd495f9f7b5453a7385b3875b0a0bd680f13b8e4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package main

import (
	"context"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	sr "tildegit.org/tjp/sliderule"
	"tildegit.org/tjp/sliderule/contrib/cgi"
	"tildegit.org/tjp/sliderule/finger"
	"tildegit.org/tjp/sliderule/logging"
)

func buildFingerServer(server Server, config *Configuration) (sr.Server, error) {
	addr := fmt.Sprintf("%s:%d", server.IP.String(), server.Port)

	_, info, _, errlog := Loggers(config)
	_ = info.Log("msg", "starting finger server", "addr", addr)

	if len(server.Routes) != 1 {
		return nil, fmt.Errorf("finger server must have 1 route directive, found %d", len(server.Routes))
	}

	if server.TLS != nil {
		return finger.NewTLSServer(
			context.Background(),
			"",
			"tcp",
			addr,
			logging.LogRequests(info)(fingerHandler(server.Routes[0])),
			errlog,
			server.TLS,
		)
	}

	return finger.NewServer(
		context.Background(),
		"",
		"tcp",
		addr,
		logging.LogRequests(info)(fingerHandler(server.Routes[0])),
		errlog,
	)
}

func fingerHandler(route RouteDirective) sr.Handler {
	if route.Type != "static" && route.Type != "cgi" {
		panic("invalid finger route type '" + route.Type + "'")
	}

	return sr.HandlerFunc(func(ctx context.Context, request *sr.Request) *sr.Response {
		u, err := user.Lookup(strings.TrimPrefix(request.Path, "/"))
		if err != nil {
			return nil
		}

		var fpath string
		if strings.HasPrefix(route.FsPath, "~/") {
			fpath = filepath.Join(u.HomeDir, route.FsPath[2:])
		} else {
			fpath = strings.Replace(route.FsPath, "~", u.Username, 1)
		}

		st, err := os.Stat(fpath)
		if err != nil {
			return finger.Error(err.Error())
		}
		if !st.Mode().IsRegular() {
			return nil
		}

		if st.Mode()&5 == 5 && (route.Modifiers.Exec || route.Type == "cgi") {
			workdir := filepath.Dir(fpath)
			if route.Modifiers.ExecCmd != "" {
				fpath = route.Modifiers.ExecCmd
			}

			buf, code, err := cgi.RunCGI(ctx, request, fpath, "/", workdir, nil)
			if err != nil {
				return finger.Error("execution error")
			}
			if code != 0 {
				return finger.Error(fmt.Sprintf("execution error: code %d", code))
			}

			return finger.Success(buf)
		}

		if route.Type != "static" {
			return nil
		}

		file, err := os.Open(fpath)
		if err != nil {
			return finger.Error(err.Error())
		}
		return finger.Success(file)
	})
}