gitee.com/larksuite/oapi-sdk-go/v3@v3.0.3/core/utils.go (about) 1 /* 2 * MIT License 3 * 4 * Copyright (c) 2022 Lark Technologies Pte. Ltd. 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 7 * 8 * The above copyright notice and this permission notice, shall be included in all copies or substantial portions of the Software. 9 * 10 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 */ 12 13 package larkcore 14 15 import ( 16 "bytes" 17 "context" 18 "crypto/aes" 19 "crypto/cipher" 20 "crypto/rand" 21 "crypto/sha256" 22 "encoding/base64" 23 "encoding/json" 24 "errors" 25 "fmt" 26 "io" 27 "io/ioutil" 28 "net/http" 29 "os" 30 "reflect" 31 "strings" 32 ) 33 34 import "time" 35 36 // StringPtr returns a pointer to the string value passed in. 37 func StringPtr(v string) *string { 38 return &v 39 } 40 41 // StringValue returns the value of the string pointer passed in or 42 // "" if the pointer is nil. 43 func StringValue(v *string) string { 44 if v != nil { 45 return *v 46 } 47 return "" 48 } 49 50 // BoolPtr returns a pointer to the bool value passed in. 51 func BoolPtr(v bool) *bool { 52 return &v 53 } 54 55 // BoolValue returns the value of the bool pointer passed in or 56 // false if the pointer is nil. 57 func BoolValue(v *bool) bool { 58 if v != nil { 59 return *v 60 } 61 return false 62 } 63 64 // IntPtr returns a pointer to the int value passed in. 65 func IntPtr(v int) *int { 66 return &v 67 } 68 69 // IntValue returns the value of the int pointer passed in or 70 // 0 if the pointer is nil. 71 func IntValue(v *int) int { 72 if v != nil { 73 return *v 74 } 75 return 0 76 } 77 78 // Int8Ptr returns a pointer to the int8 value passed in. 79 func Int8Ptr(v int8) *int8 { 80 return &v 81 } 82 83 // Int8Value returns the value of the int8 pointer passed in or 84 // 0 if the pointer is nil. 85 func Int8Value(v *int8) int8 { 86 if v != nil { 87 return *v 88 } 89 return 0 90 } 91 92 // Int16Ptr returns a pointer to the int16 value passed in. 93 func Int16Ptr(v int16) *int16 { 94 return &v 95 } 96 97 // Int16Value returns the value of the int16 pointer passed in or 98 // 0 if the pointer is nil. 99 func Int16Value(v *int16) int16 { 100 if v != nil { 101 return *v 102 } 103 return 0 104 } 105 106 // Int32Ptr returns a pointer to the int32 value passed in. 107 func Int32Ptr(v int32) *int32 { 108 return &v 109 } 110 111 // Int32Value returns the value of the int32 pointer passed in or 112 // 0 if the pointer is nil. 113 func Int32Value(v *int32) int32 { 114 if v != nil { 115 return *v 116 } 117 return 0 118 } 119 120 // Int64Ptr returns a pointer to the int64 value passed in. 121 func Int64Ptr(v int64) *int64 { 122 return &v 123 } 124 125 // Int64Value returns the value of the int64 pointer passed in or 126 // 0 if the pointer is nil. 127 func Int64Value(v *int64) int64 { 128 if v != nil { 129 return *v 130 } 131 return 0 132 } 133 134 // Float32Ptr returns a pointer to the float32 value passed in. 135 func Float32Ptr(v float32) *float32 { 136 return &v 137 } 138 139 // Float32Value returns the value of the float32 pointer passed in or 140 // 0 if the pointer is nil. 141 func Float32Value(v *float32) float32 { 142 if v != nil { 143 return *v 144 } 145 return 0 146 } 147 148 // Float64Ptr returns a pointer to the float64 value passed in. 149 func Float64Ptr(v float64) *float64 { 150 return &v 151 } 152 153 // Float64Value returns the value of the float64 pointer passed in or 154 // 0 if the pointer is nil. 155 func Float64Value(v *float64) float64 { 156 if v != nil { 157 return *v 158 } 159 return 0 160 } 161 162 // TimePtr returns a pointer to the time.Time value passed in. 163 func TimePtr(v time.Time) *time.Time { 164 return &v 165 } 166 167 // TimeValue returns the value of the time.Time pointer passed in or 168 // time.Time{} if the pointer is nil. 169 func TimeValue(v *time.Time) time.Time { 170 if v != nil { 171 return *v 172 } 173 return time.Time{} 174 } 175 176 // Prettify returns the string representation of a value. 177 func Prettify(i interface{}) string { 178 var buf bytes.Buffer 179 prettify(reflect.ValueOf(i), 0, &buf) 180 return buf.String() 181 } 182 183 // DownloadFile returns the url of resource 184 func DownloadFile(ctx context.Context, url string) ([]byte, error) { 185 r, err := downloadFileToStream(ctx, url) 186 if err != nil { 187 return nil, err 188 } 189 defer r.Close() 190 return ioutil.ReadAll(r) 191 } 192 193 type DecryptErr struct { 194 Message string 195 } 196 197 func newDecryptErr(message string) *DecryptErr { 198 return &DecryptErr{Message: message} 199 } 200 201 func (e DecryptErr) Error() string { 202 return e.Message 203 } 204 205 func downloadFileToStream(ctx context.Context, url string) (io.ReadCloser, error) { 206 request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil) 207 if err != nil { 208 return nil, err 209 } 210 resp, err := http.DefaultClient.Do(request) 211 if err != nil { 212 return nil, err 213 } 214 if resp.StatusCode != http.StatusOK { 215 return nil, fmt.Errorf("response status code:%d", resp.StatusCode) 216 } 217 return resp.Body, nil 218 } 219 220 // prettify will recursively walk value v to build a textual 221 // representation of the value. 222 func prettify(v reflect.Value, indent int, buf *bytes.Buffer) { 223 for v.Kind() == reflect.Ptr { 224 v = v.Elem() 225 } 226 switch v.Kind() { 227 case reflect.Struct: 228 strType := v.Type().String() 229 if strType == "time.Time" { 230 fmt.Fprintf(buf, "%s", v.Interface()) 231 break 232 } else if strings.HasPrefix(strType, "io.") { 233 buf.WriteString("<buffer>") 234 break 235 } 236 237 buf.WriteString("{\n") 238 239 var names []string 240 for i := 0; i < v.Type().NumField(); i++ { 241 name := v.Type().Field(i).Name 242 f := v.Field(i) 243 if name[0:1] == strings.ToLower(name[0:1]) { 244 continue // ignore unexported fields 245 } 246 if (f.Kind() == reflect.Ptr || f.Kind() == reflect.Slice || f.Kind() == reflect.Map) && f.IsNil() { 247 continue // ignore unset fields 248 } 249 names = append(names, name) 250 } 251 252 for i, n := range names { 253 val := v.FieldByName(n) 254 buf.WriteString(strings.Repeat(" ", indent+2)) 255 buf.WriteString(n + ": ") 256 prettify(val, indent+2, buf) 257 258 if i < len(names)-1 { 259 buf.WriteString(",\n") 260 } 261 } 262 263 buf.WriteString("\n" + strings.Repeat(" ", indent) + "}") 264 case reflect.Slice: 265 strType := v.Type().String() 266 if strType == "[]uint8" { 267 fmt.Fprintf(buf, "<binary> len %d", v.Len()) 268 break 269 } 270 271 nl, id, id2 := "", "", "" 272 if v.Len() > 3 { 273 nl, id, id2 = "\n", strings.Repeat(" ", indent), strings.Repeat(" ", indent+2) 274 } 275 buf.WriteString("[" + nl) 276 for i := 0; i < v.Len(); i++ { 277 buf.WriteString(id2) 278 prettify(v.Index(i), indent+2, buf) 279 280 if i < v.Len()-1 { 281 buf.WriteString("," + nl) 282 } 283 } 284 285 buf.WriteString(nl + id + "]") 286 case reflect.Map: 287 buf.WriteString("{\n") 288 289 for i, k := range v.MapKeys() { 290 buf.WriteString(strings.Repeat(" ", indent+2)) 291 buf.WriteString(k.String() + ": ") 292 prettify(v.MapIndex(k), indent+2, buf) 293 294 if i < v.Len()-1 { 295 buf.WriteString(",\n") 296 } 297 } 298 299 buf.WriteString("\n" + strings.Repeat(" ", indent) + "}") 300 default: 301 if !v.IsValid() { 302 fmt.Fprint(buf, "<invalid value>") 303 return 304 } 305 format := "%v" 306 switch v.Interface().(type) { 307 case string: 308 format = "%q" 309 case io.ReadSeeker, io.Reader: 310 format = "buffer(%p)" 311 } 312 fmt.Fprintf(buf, format, v.Interface()) 313 } 314 } 315 316 func StructToMap(val interface{}) (map[string]interface{}, error) { 317 m := make(map[string]interface{}) 318 s := reflect.Indirect(reflect.ValueOf(val)) 319 st := s.Type() 320 for i := 0; i < s.NumField(); i++ { 321 fieldDesc := st.Field(i) 322 fieldVal := s.Field(i) 323 if fieldDesc.Anonymous { 324 embeddedMap, err := StructToMap(fieldVal.Interface()) 325 if err != nil { 326 return nil, err 327 } 328 for k, v := range embeddedMap { 329 m[k] = v 330 } 331 continue 332 } 333 jsonTag := fieldDesc.Tag.Get("json") 334 if jsonTag == "" { 335 continue 336 } 337 tag, err := parseJSONTag(jsonTag) 338 if err != nil { 339 return nil, err 340 } 341 if tag.ignore { 342 continue 343 } 344 if fieldDesc.Type.Kind() == reflect.Ptr && fieldVal.IsNil() { 345 continue 346 } 347 // nil maps are treated as empty maps. 348 if fieldDesc.Type.Kind() == reflect.Map && fieldVal.IsNil() { 349 continue 350 } 351 if fieldDesc.Type.Kind() == reflect.Slice && fieldVal.IsNil() { 352 continue 353 } 354 if tag.stringFormat { 355 m[tag.name] = formatAsString(fieldVal, fieldDesc.Type.Kind()) 356 } else { 357 m[tag.name] = fieldVal.Interface() 358 } 359 } 360 return m, nil 361 } 362 363 func formatAsString(v reflect.Value, kind reflect.Kind) string { 364 if kind == reflect.Ptr && !v.IsNil() { 365 v = v.Elem() 366 } 367 return fmt.Sprintf("%v", v.Interface()) 368 } 369 370 type jsonTag struct { 371 name string 372 stringFormat bool 373 ignore bool 374 } 375 376 func parseJSONTag(val string) (jsonTag, error) { 377 if val == "-" { 378 return jsonTag{ignore: true}, nil 379 } 380 var tag jsonTag 381 i := strings.Index(val, ",") 382 if i == -1 || val[:i] == "" { 383 return tag, fmt.Errorf("malformed json tag: %s", val) 384 } 385 tag = jsonTag{ 386 name: val[:i], 387 } 388 switch val[i+1:] { 389 case "omitempty": 390 case "omitempty,string": 391 tag.stringFormat = true 392 default: 393 return tag, fmt.Errorf("malformed json tag: %s", val) 394 } 395 return tag, nil 396 } 397 398 func isEmptyValue(v reflect.Value) bool { 399 switch v.Kind() { 400 case reflect.Array, reflect.Map, reflect.Slice, reflect.String: 401 return v.Len() == 0 402 case reflect.Bool: 403 return !v.Bool() 404 case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: 405 return v.Int() == 0 406 case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: 407 return v.Uint() == 0 408 case reflect.Float32, reflect.Float64: 409 return v.Float() == 0 410 case reflect.Interface, reflect.Ptr: 411 return v.IsNil() 412 } 413 return false 414 } 415 416 func userAgent() string { 417 return fmt.Sprintf("oapi-sdk-go/%s", version) 418 } 419 420 func readResponse(resp *http.Response) ([]byte, error) { 421 defer resp.Body.Close() 422 respBody, err := ioutil.ReadAll(resp.Body) 423 if err != nil { 424 return nil, err 425 } 426 return respBody, nil 427 } 428 429 func File2Bytes(fileName string) ([]byte, error) { 430 file, err := os.Open(fileName) 431 if err != nil { 432 return nil, err 433 } 434 defer file.Close() 435 436 fileInfo, err := file.Stat() 437 if err != nil { 438 return nil, err 439 } 440 441 fileData := make([]byte, fileInfo.Size()) 442 _, err = file.Read(fileData) 443 if err != nil { 444 return nil, err 445 } 446 return fileData, nil 447 } 448 449 func standardizeDataEn(data []byte) []byte { 450 appendingLen := aes.BlockSize - (len(data) % aes.BlockSize) 451 sd := make([]byte, len(data)+appendingLen) 452 copy(sd, data) 453 for i := 0; i < appendingLen; i++ { 454 sd[i+len(data)] = byte(appendingLen) 455 } 456 return sd 457 } 458 func cBCEncrypter(buf []byte, keyStr string) ([]byte, error) { 459 key := sha256.Sum256([]byte(keyStr)) 460 plaintext := standardizeDataEn(buf) 461 462 if len(plaintext)%aes.BlockSize != 0 { 463 return nil, errors.New("plaintext is not a multiple of the block size") 464 } 465 466 block, err := aes.NewCipher(key[:sha256.Size]) 467 if err != nil { 468 return nil, err 469 } 470 471 ciphertext := make([]byte, aes.BlockSize+len(plaintext)) 472 iv := ciphertext[:aes.BlockSize] 473 if _, err := io.ReadFull(rand.Reader, iv); err != nil { 474 return nil, err 475 } 476 477 mode := cipher.NewCBCEncrypter(block, iv) 478 mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext) 479 480 if err != nil { 481 return nil, err 482 } 483 484 return ciphertext, nil 485 } 486 func EncryptedEventMsg(ctx context.Context, data interface{}, encryptKey string) (string, error) { 487 var bs []byte 488 var err error 489 490 switch data.(type) { 491 case string: 492 bs = []byte(data.(string)) 493 case []byte: 494 bs = data.([]byte) 495 default: 496 bs, err = json.Marshal(data) 497 } 498 499 if err != nil { 500 return "", err 501 } 502 503 encryptedData, err := cBCEncrypter(bs, encryptKey) 504 if err != nil { 505 return "", err 506 } 507 508 return base64.StdEncoding.EncodeToString(encryptedData), nil 509 }