1package parseutil
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7	"regexp"
8	"strconv"
9	"strings"
10	"time"
11
12	"github.com/hashicorp/errwrap"
13	sockaddr "github.com/hashicorp/go-sockaddr"
14	"github.com/hashicorp/vault/sdk/helper/strutil"
15	"github.com/mitchellh/mapstructure"
16)
17
18var validCapacityString = regexp.MustCompile("^[\t ]*([0-9]+)[\t ]?([kmgtKMGT][iI]?[bB])?[\t ]*$")
19
20// ParseCapacityString parses a capacity string and returns the number of bytes it represents.
21// Capacity strings are things like 5gib or 10MB. Supported prefixes are kb, kib, mb, mib, gb,
22// gib, tb, tib, which are not case sensitive. If no prefix is present, the number is assumed
23// to be in bytes already.
24func ParseCapacityString(in interface{}) (uint64, error) {
25	var cap uint64
26
27	jsonIn, ok := in.(json.Number)
28	if ok {
29		in = jsonIn.String()
30	}
31
32	switch inp := in.(type) {
33	case nil:
34		// return default of zero
35	case string:
36		if inp == "" {
37			return cap, nil
38		}
39
40		matches := validCapacityString.FindStringSubmatch(inp)
41
42		// no sub-groups means we couldn't parse it
43		if len(matches) <= 1 {
44			return cap, errors.New("could not parse capacity from input")
45		}
46
47		var multiplier uint64 = 1
48		switch strings.ToLower(matches[2]) {
49		case "kb":
50			multiplier = 1000
51		case "kib":
52			multiplier = 1024
53		case "mb":
54			multiplier = 1000 * 1000
55		case "mib":
56			multiplier = 1024 * 1024
57		case "gb":
58			multiplier = 1000 * 1000 * 1000
59		case "gib":
60			multiplier = 1024 * 1024 * 1024
61		case "tb":
62			multiplier = 1000 * 1000 * 1000 * 1000
63		case "tib":
64			multiplier = 1024 * 1024 * 1024 * 1024
65		}
66
67		size, err := strconv.ParseUint(matches[1], 10, 64)
68		if err != nil {
69			return cap, err
70		}
71
72		cap = size * multiplier
73	case int:
74		cap = uint64(inp)
75	case int32:
76		cap = uint64(inp)
77	case int64:
78		cap = uint64(inp)
79	case uint:
80		cap = uint64(inp)
81	case uint32:
82		cap = uint64(inp)
83	case uint64:
84		cap = uint64(inp)
85	case float32:
86		cap = uint64(inp)
87	case float64:
88		cap = uint64(inp)
89	default:
90		return cap, errors.New("could not parse capacity from input")
91	}
92
93	return cap, nil
94}
95
96func ParseDurationSecond(in interface{}) (time.Duration, error) {
97	var dur time.Duration
98	jsonIn, ok := in.(json.Number)
99	if ok {
100		in = jsonIn.String()
101	}
102	switch inp := in.(type) {
103	case nil:
104		// return default of zero
105	case string:
106		if inp == "" {
107			return dur, nil
108		}
109		var err error
110		// Look for a suffix otherwise its a plain second value
111		if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") || strings.HasSuffix(inp, "ms") {
112			dur, err = time.ParseDuration(inp)
113			if err != nil {
114				return dur, err
115			}
116		} else {
117			// Plain integer
118			secs, err := strconv.ParseInt(inp, 10, 64)
119			if err != nil {
120				return dur, err
121			}
122			dur = time.Duration(secs) * time.Second
123		}
124	case int:
125		dur = time.Duration(inp) * time.Second
126	case int32:
127		dur = time.Duration(inp) * time.Second
128	case int64:
129		dur = time.Duration(inp) * time.Second
130	case uint:
131		dur = time.Duration(inp) * time.Second
132	case uint32:
133		dur = time.Duration(inp) * time.Second
134	case uint64:
135		dur = time.Duration(inp) * time.Second
136	case float32:
137		dur = time.Duration(inp) * time.Second
138	case float64:
139		dur = time.Duration(inp) * time.Second
140	case time.Duration:
141		dur = inp
142	default:
143		return 0, errors.New("could not parse duration from input")
144	}
145
146	return dur, nil
147}
148
149func ParseAbsoluteTime(in interface{}) (time.Time, error) {
150	var t time.Time
151	switch inp := in.(type) {
152	case nil:
153		// return default of zero
154		return t, nil
155	case string:
156		// Allow RFC3339 with nanoseconds, or without,
157		// or an epoch time as an integer.
158		var err error
159		t, err = time.Parse(time.RFC3339Nano, inp)
160		if err == nil {
161			break
162		}
163		t, err = time.Parse(time.RFC3339, inp)
164		if err == nil {
165			break
166		}
167		epochTime, err := strconv.ParseInt(inp, 10, 64)
168		if err == nil {
169			t = time.Unix(epochTime, 0)
170			break
171		}
172		return t, errors.New("could not parse string as date and time")
173	case json.Number:
174		epochTime, err := inp.Int64()
175		if err != nil {
176			return t, err
177		}
178		t = time.Unix(epochTime, 0)
179	case int:
180		t = time.Unix(int64(inp), 0)
181	case int32:
182		t = time.Unix(int64(inp), 0)
183	case int64:
184		t = time.Unix(inp, 0)
185	case uint:
186		t = time.Unix(int64(inp), 0)
187	case uint32:
188		t = time.Unix(int64(inp), 0)
189	case uint64:
190		t = time.Unix(int64(inp), 0)
191	default:
192		return t, errors.New("could not parse time from input type")
193	}
194	return t, nil
195}
196
197func ParseInt(in interface{}) (int64, error) {
198	var ret int64
199	jsonIn, ok := in.(json.Number)
200	if ok {
201		in = jsonIn.String()
202	}
203	switch in.(type) {
204	case string:
205		inp := in.(string)
206		if inp == "" {
207			return 0, nil
208		}
209		var err error
210		left, err := strconv.ParseInt(inp, 10, 64)
211		if err != nil {
212			return ret, err
213		}
214		ret = left
215	case int:
216		ret = int64(in.(int))
217	case int32:
218		ret = int64(in.(int32))
219	case int64:
220		ret = in.(int64)
221	case uint:
222		ret = int64(in.(uint))
223	case uint32:
224		ret = int64(in.(uint32))
225	case uint64:
226		ret = int64(in.(uint64))
227	default:
228		return 0, errors.New("could not parse value from input")
229	}
230
231	return ret, nil
232}
233
234func ParseBool(in interface{}) (bool, error) {
235	var result bool
236	if err := mapstructure.WeakDecode(in, &result); err != nil {
237		return false, err
238	}
239	return result, nil
240}
241
242func ParseString(in interface{}) (string, error) {
243	var result string
244	if err := mapstructure.WeakDecode(in, &result); err != nil {
245		return "", err
246	}
247	return result, nil
248}
249
250func ParseCommaStringSlice(in interface{}) ([]string, error) {
251	rawString, ok := in.(string)
252	if ok && rawString == "" {
253		return []string{}, nil
254	}
255	var result []string
256	config := &mapstructure.DecoderConfig{
257		Result:           &result,
258		WeaklyTypedInput: true,
259		DecodeHook:       mapstructure.StringToSliceHookFunc(","),
260	}
261	decoder, err := mapstructure.NewDecoder(config)
262	if err != nil {
263		return nil, err
264	}
265	if err := decoder.Decode(in); err != nil {
266		return nil, err
267	}
268	return strutil.TrimStrings(result), nil
269}
270
271func ParseAddrs(addrs interface{}) ([]*sockaddr.SockAddrMarshaler, error) {
272	out := make([]*sockaddr.SockAddrMarshaler, 0)
273	stringAddrs := make([]string, 0)
274
275	switch addrs.(type) {
276	case string:
277		stringAddrs = strutil.ParseArbitraryStringSlice(addrs.(string), ",")
278		if len(stringAddrs) == 0 {
279			return nil, fmt.Errorf("unable to parse addresses from %v", addrs)
280		}
281
282	case []string:
283		stringAddrs = addrs.([]string)
284
285	case []interface{}:
286		for _, v := range addrs.([]interface{}) {
287			stringAddr, ok := v.(string)
288			if !ok {
289				return nil, fmt.Errorf("error parsing %v as string", v)
290			}
291			stringAddrs = append(stringAddrs, stringAddr)
292		}
293
294	default:
295		return nil, fmt.Errorf("unknown address input type %T", addrs)
296	}
297
298	for _, addr := range stringAddrs {
299		sa, err := sockaddr.NewSockAddr(addr)
300		if err != nil {
301			return nil, errwrap.Wrapf(fmt.Sprintf("error parsing address %q: {{err}}", addr), err)
302		}
303		out = append(out, &sockaddr.SockAddrMarshaler{
304			SockAddr: sa,
305		})
306	}
307
308	return out, nil
309}
310