~egtann/shh

ref: v1.9.0 shh/config.go -rw-r--r-- 2.7 KiB
53855162 — Evan Tann update install instructions, add man page 4 months ago
                                                                                
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
package main

import (
	"bufio"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type config struct {
	Username username
	Port     int

	// path to config directory.
	path string
}

func getGlobalConfigPath() (string, error) {
	home, err := os.UserHomeDir()
	if err != nil {
		return "", err
	}
	return filepath.Join(home, ".config", "shh"), nil
}

func getConfigPaths() (global string, project string, err error) {
	global, err = getGlobalConfigPath()
	if err != nil {
		return "", "", fmt.Errorf("global: %w", err)
	}
	project, err = getProjectConfigPath()
	if err != nil {
		return "", "", fmt.Errorf("project: %w", err)
	}
	return global, project, nil
}

func getProjectConfigPath() (string, error) {
	pth, err := findFileRecursive(".shhconfig")
	if os.IsNotExist(err) {
		// If we didn't find any project config, we're done
		return "", nil
	}
	if err != nil {
		return "", fmt.Errorf("find project config: %w", err)
	}
	return pth, nil
}

func configFromPaths(global, project string) (*config, error) {
	globalConfPath := filepath.Join(global, "config")
	fi, err := os.Open(globalConfPath)
	if os.IsNotExist(err) {
		return nil, errors.New("missing keys. run `shh gen-keys`")
	}
	if err != nil {
		return nil, fmt.Errorf("open config: %w", err)
	}
	defer fi.Close()
	globalConf, err := parseConfig(fi)
	if err != nil {
		return nil, fmt.Errorf("parse config: %w", err)
	}
	globalConf.path = global

	// If there's no project-specific config, we're done
	if project == "" {
		return globalConf, nil
	}
	fi2, err := os.Open(project)
	if err != nil {
		return nil, fmt.Errorf("open project config: %w", err)
	}
	defer fi2.Close()
	projectConf, err := parseConfig(fi2)
	if err != nil {
		return nil, fmt.Errorf("parse project config: %w", err)
	}

	// Override global config with project-specific settings. Only the
	// username can be overridden now.
	if projectConf.Username != "" {
		globalConf.Username = projectConf.Username
	}

	return globalConf, nil
}

func parseConfig(r io.Reader) (*config, error) {
	conf := &config{}
	scn := bufio.NewScanner(r)
	for i := 1; scn.Scan(); i++ {
		line := scn.Text()
		if line == "" {
			continue
		}
		parts := strings.SplitN(line, "=", 2)
		if len(parts) != 2 {
			return nil, fmt.Errorf("invalid line %d: %s", i, line)
		}
		parts[0] = strings.TrimSpace(parts[0])
		parts[1] = strings.TrimSpace(parts[1])
		switch parts[0] {
		case "username":
			conf.Username = username(parts[1])
		case "port":
			var err error
			conf.Port, err = strconv.Atoi(parts[1])
			if err != nil {
				return nil, fmt.Errorf("invalid port %s: %w", parts[1], err)
			}
		default:
			return nil, fmt.Errorf("unknown part %s", parts[0])
		}
	}
	if err := scn.Err(); err != nil {
		return nil, fmt.Errorf("scan: %w", err)
	}
	return conf, nil
}