1package parseutil
2
3import (
4	"encoding/json"
5	"errors"
6	"fmt"
7	"strconv"
8	"strings"
9	"time"
10
11	"github.com/hashicorp/errwrap"
12	sockaddr "github.com/hashicorp/go-sockaddr"
13	"github.com/hashicorp/vault/sdk/helper/strutil"
14	"github.com/mitchellh/mapstructure"
15)
16
17func ParseDurationSecond(in interface{}) (time.Duration, error) {
18	var dur time.Duration
19	jsonIn, ok := in.(json.Number)
20	if ok {
21		in = jsonIn.String()
22	}
23	switch inp := in.(type) {
24	case nil:
25		// return default of zero
26	case string:
27		if inp == "" {
28			return dur, nil
29		}
30		var err error
31		// Look for a suffix otherwise its a plain second value
32		if strings.HasSuffix(inp, "s") || strings.HasSuffix(inp, "m") || strings.HasSuffix(inp, "h") || strings.HasSuffix(inp, "ms") {
33			dur, err = time.ParseDuration(inp)
34			if err != nil {
35				return dur, err
36			}
37		} else {
38			// Plain integer
39			secs, err := strconv.ParseInt(inp, 10, 64)
40			if err != nil {
41				return dur, err
42			}
43			dur = time.Duration(secs) * time.Second
44		}
45	case int:
46		dur = time.Duration(inp) * time.Second
47	case int32:
48		dur = time.Duration(inp) * time.Second
49	case int64:
50		dur = time.Duration(inp) * time.Second
51	case uint:
52		dur = time.Duration(inp) * time.Second
53	case uint32:
54		dur = time.Duration(inp) * time.Second
55	case uint64:
56		dur = time.Duration(inp) * time.Second
57	case float32:
58		dur = time.Duration(inp) * time.Second
59	case float64:
60		dur = time.Duration(inp) * time.Second
61	case time.Duration:
62		dur = inp
63	default:
64		return 0, errors.New("could not parse duration from input")
65	}
66
67	return dur, nil
68}
69
70func ParseAbsoluteTime(in interface{}) (time.Time, error) {
71	var t time.Time
72	switch inp := in.(type) {
73	case nil:
74		// return default of zero
75		return t, nil
76	case string:
77		// Allow RFC3339 with nanoseconds, or without,
78		// or an epoch time as an integer.
79		var err error
80		t, err = time.Parse(time.RFC3339Nano, inp)
81		if err == nil {
82			break
83		}
84		t, err = time.Parse(time.RFC3339, inp)
85		if err == nil {
86			break
87		}
88		epochTime, err := strconv.ParseInt(inp, 10, 64)
89		if err == nil {
90			t = time.Unix(epochTime, 0)
91			break
92		}
93		return t, errors.New("could not parse string as date and time")
94	case json.Number:
95		epochTime, err := inp.Int64()
96		if err != nil {
97			return t, err
98		}
99		t = time.Unix(epochTime, 0)
100	case int:
101		t = time.Unix(int64(inp), 0)
102	case int32:
103		t = time.Unix(int64(inp), 0)
104	case int64:
105		t = time.Unix(inp, 0)
106	case uint:
107		t = time.Unix(int64(inp), 0)
108	case uint32:
109		t = time.Unix(int64(inp), 0)
110	case uint64:
111		t = time.Unix(int64(inp), 0)
112	default:
113		return t, errors.New("could not parse time from input type")
114	}
115	return t, nil
116}
117
118func ParseInt(in interface{}) (int64, error) {
119	var ret int64
120	jsonIn, ok := in.(json.Number)
121	if ok {
122		in = jsonIn.String()
123	}
124	switch in.(type) {
125	case string:
126		inp := in.(string)
127		if inp == "" {
128			return 0, nil
129		}
130		var err error
131		left, err := strconv.ParseInt(inp, 10, 64)
132		if err != nil {
133			return ret, err
134		}
135		ret = left
136	case int:
137		ret = int64(in.(int))
138	case int32:
139		ret = int64(in.(int32))
140	case int64:
141		ret = in.(int64)
142	case uint:
143		ret = int64(in.(uint))
144	case uint32:
145		ret = int64(in.(uint32))
146	case uint64:
147		ret = int64(in.(uint64))
148	default:
149		return 0, errors.New("could not parse value from input")
150	}
151
152	return ret, nil
153}
154
155func ParseBool(in interface{}) (bool, error) {
156	var result bool
157	if err := mapstructure.WeakDecode(in, &result); err != nil {
158		return false, err
159	}
160	return result, nil
161}
162
163func ParseString(in interface{}) (string, error) {
164	var result string
165	if err := mapstructure.WeakDecode(in, &result); err != nil {
166		return "", err
167	}
168	return result, nil
169}
170
171func ParseCommaStringSlice(in interface{}) ([]string, error) {
172	rawString, ok := in.(string)
173	if ok && rawString == "" {
174		return []string{}, nil
175	}
176	var result []string
177	config := &mapstructure.DecoderConfig{
178		Result:           &result,
179		WeaklyTypedInput: true,
180		DecodeHook:       mapstructure.StringToSliceHookFunc(","),
181	}
182	decoder, err := mapstructure.NewDecoder(config)
183	if err != nil {
184		return nil, err
185	}
186	if err := decoder.Decode(in); err != nil {
187		return nil, err
188	}
189	return strutil.TrimStrings(result), nil
190}
191
192func ParseAddrs(addrs interface{}) ([]*sockaddr.SockAddrMarshaler, error) {
193	out := make([]*sockaddr.SockAddrMarshaler, 0)
194	stringAddrs := make([]string, 0)
195
196	switch addrs.(type) {
197	case string:
198		stringAddrs = strutil.ParseArbitraryStringSlice(addrs.(string), ",")
199		if len(stringAddrs) == 0 {
200			return nil, fmt.Errorf("unable to parse addresses from %v", addrs)
201		}
202
203	case []string:
204		stringAddrs = addrs.([]string)
205
206	case []interface{}:
207		for _, v := range addrs.([]interface{}) {
208			stringAddr, ok := v.(string)
209			if !ok {
210				return nil, fmt.Errorf("error parsing %v as string", v)
211			}
212			stringAddrs = append(stringAddrs, stringAddr)
213		}
214
215	default:
216		return nil, fmt.Errorf("unknown address input type %T", addrs)
217	}
218
219	for _, addr := range stringAddrs {
220		sa, err := sockaddr.NewSockAddr(addr)
221		if err != nil {
222			return nil, errwrap.Wrapf(fmt.Sprintf("error parsing address %q: {{err}}", addr), err)
223		}
224		out = append(out, &sockaddr.SockAddrMarshaler{
225			SockAddr: sa,
226		})
227	}
228
229	return out, nil
230}
231