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