1// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
2// See LICENSE.txt for license information.
3
4package config
5
6import (
7	"os"
8	"reflect"
9	"strconv"
10	"strings"
11
12	"github.com/mattermost/mattermost-server/v6/model"
13)
14
15func GetEnvironment() map[string]string {
16	mmenv := make(map[string]string)
17	for _, env := range os.Environ() {
18		kv := strings.SplitN(env, "=", 2)
19		key := strings.ToUpper(kv[0])
20		if strings.HasPrefix(key, "MM") {
21			mmenv[key] = kv[1]
22		}
23	}
24
25	return mmenv
26}
27
28func applyEnvKey(key, value string, rValueSubject reflect.Value) {
29	keyParts := strings.SplitN(key, "_", 2)
30	if len(keyParts) < 1 {
31		return
32	}
33	rFieldValue := rValueSubject.FieldByNameFunc(func(candidate string) bool {
34		candidateUpper := strings.ToUpper(candidate)
35		return candidateUpper == keyParts[0]
36	})
37
38	if !rFieldValue.IsValid() {
39		return
40	}
41
42	if rFieldValue.Kind() == reflect.Ptr {
43		rFieldValue = rFieldValue.Elem()
44		if !rFieldValue.IsValid() {
45			return
46		}
47	}
48
49	switch rFieldValue.Kind() {
50	case reflect.Struct:
51		// If we have only one part left, we can't deal with a struct
52		// the env var is incomplete so give up.
53		if len(keyParts) < 2 {
54			return
55		}
56		applyEnvKey(keyParts[1], value, rFieldValue)
57	case reflect.String:
58		rFieldValue.Set(reflect.ValueOf(value))
59	case reflect.Bool:
60		boolVal, err := strconv.ParseBool(value)
61		if err == nil {
62			rFieldValue.Set(reflect.ValueOf(boolVal))
63		}
64	case reflect.Int:
65		intVal, err := strconv.ParseInt(value, 10, 0)
66		if err == nil {
67			rFieldValue.Set(reflect.ValueOf(int(intVal)))
68		}
69	case reflect.Int64:
70		intVal, err := strconv.ParseInt(value, 10, 0)
71		if err == nil {
72			rFieldValue.Set(reflect.ValueOf(intVal))
73		}
74	case reflect.SliceOf(reflect.TypeOf("")).Kind():
75		rFieldValue.Set(reflect.ValueOf(strings.Split(value, " ")))
76	}
77}
78
79func applyEnvironmentMap(inputConfig *model.Config, env map[string]string) *model.Config {
80	appliedConfig := inputConfig.Clone()
81
82	rvalConfig := reflect.ValueOf(appliedConfig).Elem()
83	for envKey, envValue := range env {
84		applyEnvKey(strings.TrimPrefix(envKey, "MM_"), envValue, rvalConfig)
85	}
86
87	return appliedConfig
88}
89
90// generateEnvironmentMap creates a map[string]interface{} containing true at the leaves mirroring the
91// configuration structure so the client can know which env variables are overridden
92func generateEnvironmentMap(env map[string]string, filter func(reflect.StructField) bool) map[string]interface{} {
93	rType := reflect.TypeOf(model.Config{})
94	return generateEnvironmentMapWithBaseKey(env, rType, "MM", filter)
95}
96
97func generateEnvironmentMapWithBaseKey(env map[string]string, rType reflect.Type, base string, filter func(reflect.StructField) bool) map[string]interface{} {
98	if rType.Kind() != reflect.Struct {
99		return nil
100	}
101
102	mapRepresentation := make(map[string]interface{})
103	for i := 0; i < rType.NumField(); i++ {
104		rField := rType.Field(i)
105		if filter != nil && !filter(rField) {
106			continue
107		}
108		if rField.Type.Kind() == reflect.Struct {
109			if val := generateEnvironmentMapWithBaseKey(env, rField.Type, base+"_"+rField.Name, filter); val != nil {
110				mapRepresentation[rField.Name] = val
111			}
112		} else {
113			if _, ok := env[strings.ToUpper(base+"_"+rField.Name)]; ok {
114				mapRepresentation[rField.Name] = true
115			}
116		}
117	}
118
119	if len(mapRepresentation) == 0 {
120		return nil
121	}
122
123	return mapRepresentation
124}
125
126// removeEnvOverrides returns a new config without the given environment overrides.
127// If a config variable has an environment override, that variable is set to the value that was
128// read from the store.
129func removeEnvOverrides(cfg, cfgWithoutEnv *model.Config, envOverrides map[string]interface{}) *model.Config {
130	paths := getPaths(envOverrides)
131	newCfg := cfg.Clone()
132	for _, path := range paths {
133		originalVal := getVal(cfgWithoutEnv, path)
134		newVal := getVal(newCfg, path)
135		if newVal.CanSet() {
136			newVal.Set(originalVal)
137		}
138	}
139	return newCfg
140}
141
142// getPaths turns a nested map into a slice of paths describing the keys of the map. Eg:
143// map[string]map[string]map[string]bool{"this":{"is first":{"path":true}, "is second":{"path":true}))) is turned into:
144// [][]string{{"this", "is first", "path"}, {"this", "is second", "path"}}
145func getPaths(m map[string]interface{}) [][]string {
146	return getPathsRec(m, nil)
147}
148
149// getPathsRec assembles the paths (see `getPaths` above)
150func getPathsRec(src interface{}, curPath []string) [][]string {
151	if srcMap, ok := src.(map[string]interface{}); ok {
152		paths := [][]string{}
153		for k, v := range srcMap {
154			paths = append(paths, getPathsRec(v, append(curPath, k))...)
155		}
156		return paths
157	}
158
159	return [][]string{curPath}
160}
161
162// getVal walks `src` (here it starts with a model.Config, then recurses into its leaves)
163// and returns the reflect.Value of the leaf at the end `path`
164func getVal(src interface{}, path []string) reflect.Value {
165	var val reflect.Value
166
167	// If we recursed on a Value, we already have it. If we're calling on an interface{}, get the Value.
168	switch v := src.(type) {
169	case reflect.Value:
170		val = v
171	default:
172		val = reflect.ValueOf(src)
173	}
174
175	// Move into the struct
176	if val.Kind() == reflect.Ptr {
177		val = val.Elem().FieldByName(path[0])
178	} else {
179		val = val.FieldByName(path[0])
180	}
181	if val.Kind() == reflect.Ptr {
182		val = val.Elem()
183	}
184	if val.Kind() == reflect.Struct {
185		return getVal(val, path[1:])
186	}
187	return val
188}
189