github.com/chanxuehong/wechat@v0.0.0-20230222024006-36f0325263cd/mp/core/server.go (about) 1 package core 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/xml" 7 "errors" 8 "fmt" 9 "io" 10 "io/ioutil" 11 "log" 12 "net/http" 13 "net/url" 14 "strconv" 15 "sync" 16 "sync/atomic" 17 "unicode" 18 "unsafe" 19 20 "github.com/chanxuehong/util/security" 21 22 "github.com/chanxuehong/wechat/internal/debug/callback" 23 "github.com/chanxuehong/wechat/internal/util" 24 ) 25 26 // Server 用于处理微信服务器的回调请求, 并发安全! 27 // 28 // 通常情况下一个 Server 实例用于处理一个公众号的消息(事件), 此时建议指定 oriId(原始ID) 和 appId(明文模式下无需指定) 用于约束消息(事件); 29 // 特殊情况下也可以一个 Server 实例用于处理多个公众号的消息(事件), 此时要求这些公众号的 token 是一样的, 并且 oriId 和 appId 必须设置为 "". 30 type Server struct { 31 oriId string 32 appId string 33 34 tokenBucketPtrMutex sync.Mutex // used only by writers 35 tokenBucketPtr unsafe.Pointer // *tokenBucket 36 37 aesKeyBucketPtrMutex sync.Mutex // used only by writers 38 aesKeyBucketPtr unsafe.Pointer // *aesKeyBucket 39 40 handler Handler 41 errorHandler ErrorHandler 42 } 43 44 func (srv *Server) OriId() string { 45 return srv.oriId 46 } 47 func (srv *Server) AppId() string { 48 return srv.appId 49 } 50 51 type tokenBucket struct { 52 currentToken string 53 lastToken string 54 } 55 56 type aesKeyBucket struct { 57 currentAESKey []byte 58 lastAESKey []byte 59 } 60 61 // NewServer 创建一个新的 Server. 62 // 63 // oriId: 可选; 公众号的原始ID(微信公众号管理后台查看), 如果设置了值则该Server只能处理 ToUserName 为该值的公众号的消息(事件); 64 // appId: 可选; 公众号的AppId, 如果设置了值则安全模式时该Server只能处理 AppId 为该值的公众号的消息(事件); 65 // token: 必须; 公众号用于验证签名的token; 66 // base64AESKey: 可选; aes加密解密key, 43字节长(base64编码, 去掉了尾部的'='), 安全模式必须设置; 67 // handler: 必须; 处理微信服务器推送过来的消息(事件)的Handler; 68 // errorHandler: 可选; 用于处理Server在处理消息(事件)过程中产生的错误, 如果没有设置则默认使用 DefaultErrorHandler. 69 func NewServer(oriId, appId, token, base64AESKey string, handler Handler, errorHandler ErrorHandler) (srv *Server) { 70 if token == "" { 71 panic("empty token") 72 } 73 if handler == nil { 74 panic("nil Handler") 75 } 76 if errorHandler == nil { 77 errorHandler = DefaultErrorHandler 78 } 79 80 var ( 81 aesKey []byte 82 err error 83 ) 84 if base64AESKey != "" { 85 if len(base64AESKey) != 43 { 86 panic("the length of base64AESKey must equal to 43") 87 } 88 aesKey, err = base64.StdEncoding.DecodeString(base64AESKey + "=") 89 if err != nil { 90 panic(fmt.Sprintf("Decode base64AESKey:%s failed", base64AESKey)) 91 } 92 } 93 94 return &Server{ 95 oriId: oriId, 96 appId: appId, 97 tokenBucketPtr: unsafe.Pointer(&tokenBucket{currentToken: token}), 98 aesKeyBucketPtr: unsafe.Pointer(&aesKeyBucket{currentAESKey: aesKey}), 99 handler: handler, 100 errorHandler: errorHandler, 101 } 102 } 103 104 func (srv *Server) getToken() (currentToken, lastToken string) { 105 if p := (*tokenBucket)(atomic.LoadPointer(&srv.tokenBucketPtr)); p != nil { 106 return p.currentToken, p.lastToken 107 } 108 return 109 } 110 111 // SetToken 设置签名token. 112 func (srv *Server) SetToken(token string) (err error) { 113 if token == "" { 114 return errors.New("empty token") 115 } 116 117 srv.tokenBucketPtrMutex.Lock() 118 defer srv.tokenBucketPtrMutex.Unlock() 119 120 currentToken, _ := srv.getToken() 121 if token == currentToken { 122 return 123 } 124 125 bucket := tokenBucket{ 126 currentToken: token, 127 lastToken: currentToken, 128 } 129 atomic.StorePointer(&srv.tokenBucketPtr, unsafe.Pointer(&bucket)) 130 return 131 } 132 133 func (srv *Server) removeLastToken(lastToken string) { 134 srv.tokenBucketPtrMutex.Lock() 135 defer srv.tokenBucketPtrMutex.Unlock() 136 137 currentToken2, lastToken2 := srv.getToken() 138 if lastToken != lastToken2 { 139 return 140 } 141 142 bucket := tokenBucket{ 143 currentToken: currentToken2, 144 } 145 atomic.StorePointer(&srv.tokenBucketPtr, unsafe.Pointer(&bucket)) 146 return 147 } 148 149 func (srv *Server) getAESKey() (currentAESKey, lastAESKey []byte) { 150 if p := (*aesKeyBucket)(atomic.LoadPointer(&srv.aesKeyBucketPtr)); p != nil { 151 return p.currentAESKey, p.lastAESKey 152 } 153 return 154 } 155 156 // SetAESKey 设置aes加密解密key. 157 // 158 // base64AESKey: aes加密解密key, 43字节长(base64编码, 去掉了尾部的'='). 159 func (srv *Server) SetAESKey(base64AESKey string) (err error) { 160 if len(base64AESKey) != 43 { 161 return errors.New("the length of base64AESKey must equal to 43") 162 } 163 aesKey, err := base64.StdEncoding.DecodeString(base64AESKey + "=") 164 if err != nil { 165 return 166 } 167 168 srv.aesKeyBucketPtrMutex.Lock() 169 defer srv.aesKeyBucketPtrMutex.Unlock() 170 171 currentAESKey, _ := srv.getAESKey() 172 if bytes.Equal(aesKey, currentAESKey) { 173 return 174 } 175 176 bucket := aesKeyBucket{ 177 currentAESKey: aesKey, 178 lastAESKey: currentAESKey, 179 } 180 atomic.StorePointer(&srv.aesKeyBucketPtr, unsafe.Pointer(&bucket)) 181 return 182 } 183 184 func (srv *Server) removeLastAESKey(lastAESKey []byte) { 185 srv.aesKeyBucketPtrMutex.Lock() 186 defer srv.aesKeyBucketPtrMutex.Unlock() 187 188 currentAESKey2, lastAESKey2 := srv.getAESKey() 189 if !bytes.Equal(lastAESKey, lastAESKey2) { 190 return 191 } 192 193 bucket := aesKeyBucket{ 194 currentAESKey: currentAESKey2, 195 } 196 atomic.StorePointer(&srv.aesKeyBucketPtr, unsafe.Pointer(&bucket)) 197 return 198 } 199 200 // ServeHTTP 处理微信服务器的回调请求, query 参数可以为 nil. 201 func (srv *Server) ServeHTTP(w http.ResponseWriter, r *http.Request, query url.Values) { 202 callback.DebugPrintRequest(r) 203 if query == nil { 204 query = r.URL.Query() 205 } 206 errorHandler := srv.errorHandler 207 208 switch r.Method { 209 case "POST": // 推送消息(事件) 210 switch encryptType := query.Get("encrypt_type"); encryptType { 211 case "aes": 212 haveSignature := query.Get("signature") 213 if haveSignature == "" { 214 errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) 215 return 216 } 217 haveMsgSignature := query.Get("msg_signature") 218 if haveMsgSignature == "" { 219 errorHandler.ServeError(w, r, errors.New("not found msg_signature query parameter")) 220 return 221 } 222 timestampString := query.Get("timestamp") 223 if timestampString == "" { 224 errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) 225 return 226 } 227 timestamp, err := strconv.ParseInt(timestampString, 10, 64) 228 if err != nil { 229 err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString) 230 errorHandler.ServeError(w, r, err) 231 return 232 } 233 nonce := query.Get("nonce") 234 if nonce == "" { 235 errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) 236 return 237 } 238 239 var token string 240 currentToken, lastToken := srv.getToken() 241 if currentToken == "" { 242 err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method") 243 errorHandler.ServeError(w, r, err) 244 return 245 } 246 token = currentToken 247 wantSignature := util.Sign(token, timestampString, nonce) 248 if !security.SecureCompareString(haveSignature, wantSignature) { 249 if lastToken == "" { 250 err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 251 errorHandler.ServeError(w, r, err) 252 return 253 } 254 token = lastToken 255 wantSignature = util.Sign(token, timestampString, nonce) 256 if !security.SecureCompareString(haveSignature, wantSignature) { 257 err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 258 errorHandler.ServeError(w, r, err) 259 return 260 } 261 } else { 262 if lastToken != "" { 263 srv.removeLastToken(lastToken) 264 } 265 } 266 267 buffer := textBufferPool.Get().(*bytes.Buffer) 268 buffer.Reset() 269 defer textBufferPool.Put(buffer) 270 271 if _, err = buffer.ReadFrom(r.Body); err != nil { 272 errorHandler.ServeError(w, r, err) 273 return 274 } 275 requestBodyBytes := buffer.Bytes() 276 277 var requestHttpBody cipherRequestHttpBody 278 if err = xmlUnmarshal(requestBodyBytes, &requestHttpBody); err != nil { 279 errorHandler.ServeError(w, r, err) 280 return 281 } 282 283 haveToUserName := requestHttpBody.ToUserName 284 wantToUserName := srv.oriId 285 if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) { 286 err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s", 287 haveToUserName, wantToUserName) 288 errorHandler.ServeError(w, r, err) 289 return 290 } 291 292 wantMsgSignature := util.MsgSign(token, timestampString, nonce, string(requestHttpBody.Base64EncryptedMsg)) 293 if !security.SecureCompareString(haveMsgSignature, wantMsgSignature) { 294 err = fmt.Errorf("check msg_signature failed, have: %s, want: %s", haveMsgSignature, wantMsgSignature) 295 errorHandler.ServeError(w, r, err) 296 return 297 } 298 299 encryptedMsg := make([]byte, base64.StdEncoding.DecodedLen(len(requestHttpBody.Base64EncryptedMsg))) 300 encryptedMsgLen, err := base64.StdEncoding.Decode(encryptedMsg, requestHttpBody.Base64EncryptedMsg) 301 if err != nil { 302 errorHandler.ServeError(w, r, err) 303 return 304 } 305 encryptedMsg = encryptedMsg[:encryptedMsgLen] 306 307 var aesKey []byte 308 currentAESKey, lastAESKey := srv.getAESKey() 309 if currentAESKey == nil { 310 err = errors.New("aes key was not set for Server, see NewServer function or Server.SetAESKey method") 311 errorHandler.ServeError(w, r, err) 312 return 313 } 314 aesKey = currentAESKey 315 random, msgPlaintext, haveAppIdBytes, err := util.AESDecryptMsg(encryptedMsg, aesKey) 316 if err != nil { 317 if lastAESKey == nil { 318 errorHandler.ServeError(w, r, err) 319 return 320 } 321 aesKey = lastAESKey 322 random, msgPlaintext, haveAppIdBytes, err = util.AESDecryptMsg(encryptedMsg, aesKey) 323 if err != nil { 324 errorHandler.ServeError(w, r, err) 325 return 326 } 327 } else { 328 if lastAESKey != nil { 329 srv.removeLastAESKey(lastAESKey) 330 } 331 } 332 callback.DebugPrintPlainRequestMessage(msgPlaintext) 333 334 haveAppId := string(haveAppIdBytes) 335 wantAppId := srv.appId 336 if wantAppId != "" && !security.SecureCompareString(haveAppId, wantAppId) { 337 err = fmt.Errorf("the message AppId mismatch, have: %s, want: %s", haveAppId, wantAppId) 338 errorHandler.ServeError(w, r, err) 339 return 340 } 341 342 var mixedMsg MixedMsg 343 if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil { 344 errorHandler.ServeError(w, r, err) 345 return 346 } 347 if haveToUserName != mixedMsg.ToUserName { 348 err = fmt.Errorf("the message ToUserName mismatch between ciphertext and plaintext, %q != %q", 349 haveToUserName, mixedMsg.ToUserName) 350 errorHandler.ServeError(w, r, err) 351 return 352 } 353 354 ctx := &Context{ 355 ResponseWriter: w, 356 Request: r, 357 358 QueryParams: query, 359 EncryptType: encryptType, 360 MsgSignature: haveMsgSignature, 361 Signature: haveSignature, 362 Timestamp: timestamp, 363 Nonce: nonce, 364 365 MsgCiphertext: requestHttpBody.Base64EncryptedMsg, 366 MsgPlaintext: msgPlaintext, 367 MixedMsg: &mixedMsg, 368 369 Token: token, 370 AESKey: aesKey, 371 Random: random, 372 AppId: haveAppId, 373 374 handlerIndex: initHandlerIndex, 375 } 376 srv.handler.ServeMsg(ctx) 377 378 case "", "raw": 379 haveSignature := query.Get("signature") 380 if haveSignature == "" { 381 errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) 382 return 383 } 384 timestampString := query.Get("timestamp") 385 if timestampString == "" { 386 errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) 387 return 388 } 389 timestamp, err := strconv.ParseInt(timestampString, 10, 64) 390 if err != nil { 391 err = fmt.Errorf("can not parse timestamp query parameter %q to int64", timestampString) 392 errorHandler.ServeError(w, r, err) 393 return 394 } 395 nonce := query.Get("nonce") 396 if nonce == "" { 397 errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) 398 return 399 } 400 401 var token string 402 currentToken, lastToken := srv.getToken() 403 if currentToken == "" { 404 err = errors.New("token was not set for Server, see NewServer function or Server.SetToken method") 405 errorHandler.ServeError(w, r, err) 406 return 407 } 408 token = currentToken 409 wantSignature := util.Sign(token, timestampString, nonce) 410 if !security.SecureCompareString(haveSignature, wantSignature) { 411 if lastToken == "" { 412 err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 413 errorHandler.ServeError(w, r, err) 414 return 415 } 416 token = lastToken 417 wantSignature = util.Sign(token, timestampString, nonce) 418 if !security.SecureCompareString(haveSignature, wantSignature) { 419 err = fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 420 errorHandler.ServeError(w, r, err) 421 return 422 } 423 } else { 424 if lastToken != "" { 425 srv.removeLastToken(lastToken) 426 } 427 } 428 429 msgPlaintext, err := ioutil.ReadAll(r.Body) 430 if err != nil { 431 errorHandler.ServeError(w, r, err) 432 return 433 } 434 callback.DebugPrintPlainRequestMessage(msgPlaintext) 435 436 var mixedMsg MixedMsg 437 if err = xml.Unmarshal(msgPlaintext, &mixedMsg); err != nil { 438 errorHandler.ServeError(w, r, err) 439 return 440 } 441 442 haveToUserName := mixedMsg.ToUserName 443 wantToUserName := srv.oriId 444 if wantToUserName != "" && !security.SecureCompareString(haveToUserName, wantToUserName) { 445 err = fmt.Errorf("the message ToUserName mismatch, have: %s, want: %s", 446 haveToUserName, wantToUserName) 447 errorHandler.ServeError(w, r, err) 448 return 449 } 450 451 ctx := &Context{ 452 ResponseWriter: w, 453 Request: r, 454 455 QueryParams: query, 456 EncryptType: encryptType, 457 Signature: haveSignature, 458 Timestamp: timestamp, 459 Nonce: nonce, 460 461 MsgPlaintext: msgPlaintext, 462 MixedMsg: &mixedMsg, 463 464 Token: token, 465 466 handlerIndex: initHandlerIndex, 467 } 468 srv.handler.ServeMsg(ctx) 469 470 default: 471 errorHandler.ServeError(w, r, errors.New("unknown encrypt_type: "+encryptType)) 472 } 473 474 case "GET": // 验证回调URL是否有效 475 haveSignature := query.Get("signature") 476 if haveSignature == "" { 477 errorHandler.ServeError(w, r, errors.New("not found signature query parameter")) 478 return 479 } 480 timestamp := query.Get("timestamp") 481 if timestamp == "" { 482 errorHandler.ServeError(w, r, errors.New("not found timestamp query parameter")) 483 return 484 } 485 nonce := query.Get("nonce") 486 if nonce == "" { 487 errorHandler.ServeError(w, r, errors.New("not found nonce query parameter")) 488 return 489 } 490 echostr := query.Get("echostr") 491 if echostr == "" { 492 errorHandler.ServeError(w, r, errors.New("not found echostr query parameter")) 493 return 494 } 495 496 var token string 497 currentToken, lastToken := srv.getToken() 498 if currentToken == "" { 499 err := errors.New("token was not set for Server, see NewServer function or Server.SetToken method") 500 errorHandler.ServeError(w, r, err) 501 return 502 } 503 token = currentToken 504 wantSignature := util.Sign(token, timestamp, nonce) 505 if !security.SecureCompareString(haveSignature, wantSignature) { 506 if lastToken == "" { 507 err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 508 errorHandler.ServeError(w, r, err) 509 return 510 } 511 token = lastToken 512 wantSignature = util.Sign(token, timestamp, nonce) 513 if !security.SecureCompareString(haveSignature, wantSignature) { 514 err := fmt.Errorf("check signature failed, have: %s, want: %s", haveSignature, wantSignature) 515 errorHandler.ServeError(w, r, err) 516 return 517 } 518 } else { 519 if lastToken != "" { 520 srv.removeLastToken(lastToken) 521 } 522 } 523 524 io.WriteString(w, echostr) 525 } 526 } 527 528 // ===================================================================================================================== 529 530 type cipherRequestHttpBody struct { 531 XMLName struct{} `xml:"xml"` 532 ToUserName string `xml:"ToUserName"` 533 Base64EncryptedMsg []byte `xml:"Encrypt"` 534 } 535 536 var ( 537 msgStartElementLiteral = []byte("<xml>") 538 msgEndElementLiteral = []byte("</xml>") 539 540 msgToUserNameStartElementLiteral = []byte("<ToUserName>") 541 msgToUserNameEndElementLiteral = []byte("</ToUserName>") 542 543 msgEncryptStartElementLiteral = []byte("<Encrypt>") 544 msgEncryptEndElementLiteral = []byte("</Encrypt>") 545 546 cdataStartLiteral = []byte("<![CDATA[") 547 cdataEndLiteral = []byte("]]>") 548 ) 549 550 // <xml> 551 // 552 // <ToUserName><![CDATA[gh_b1eb3f8bd6c6]]></ToUserName> 553 // <Encrypt><![CDATA[DlCGq+lWQuyjNNK+vDaO0zUltpdUW3u4V00WCzsdNzmZGEhrU7TPxG52viOKCWYPwTMbCzgbCtakZHyNxr5hjoZJ7ORAUYoIAGQy/LDWtAnYgDO+ppKLp0rDq+67Dv3yt+vatMQTh99NII6x9SEGpY3O2h8RpG99+NYevQiOLVKqiQYzan21sX/jE4Y3wZaeudsb4QVjqzRAPaCJ5nS3T31uIR9fjSRgHTDRDOzjQ1cHchge+t6faUhniN5VQVTE+wIYtmnejc55BmHYPfBnTkYah9+cTYnI3diUPJRRiyVocJyHlb+XOZN22dsx9yzKHBAyagaoDIV8Yyb/PahcUbsqGv5wziOgLJQIa6z93/VY7d2Kq2C2oBS+Qb+FI9jLhgc3RvCi+Yno2X3cWoqbsRwoovYdyg6jme/H7nMZn77PSxOGRt/dYiWx2NuBAF7fNFigmbRiive3DyOumNCMvA==]]></Encrypt> 554 // 555 // </xml> 556 func xmlUnmarshal(data []byte, p *cipherRequestHttpBody) error { 557 data = bytes.TrimSpace(data) 558 if !bytes.HasPrefix(data, msgStartElementLiteral) || !bytes.HasSuffix(data, msgEndElementLiteral) { 559 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 560 return xml.Unmarshal(data, p) 561 } 562 data2 := data[len(msgStartElementLiteral) : len(data)-len(msgEndElementLiteral)] 563 564 // ToUserName 565 ToUserNameElementBytes := data2 566 i := bytes.Index(ToUserNameElementBytes, msgToUserNameStartElementLiteral) 567 if i == -1 { 568 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 569 return xml.Unmarshal(data, p) 570 } 571 ToUserNameElementBytes = ToUserNameElementBytes[i+len(msgToUserNameStartElementLiteral):] 572 ToUserNameElementBytes = bytes.TrimLeftFunc(ToUserNameElementBytes, unicode.IsSpace) 573 if !bytes.HasPrefix(ToUserNameElementBytes, cdataStartLiteral) { 574 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 575 return xml.Unmarshal(data, p) 576 } 577 ToUserNameElementBytes = ToUserNameElementBytes[len(cdataStartLiteral):] 578 i = bytes.Index(ToUserNameElementBytes, cdataEndLiteral) 579 if i == -1 { 580 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 581 return xml.Unmarshal(data, p) 582 } 583 ToUserName := ToUserNameElementBytes[:i] 584 ToUserNameElementBytes = ToUserNameElementBytes[i+len(cdataEndLiteral):] 585 ToUserNameElementBytes = bytes.TrimLeftFunc(ToUserNameElementBytes, unicode.IsSpace) 586 if !bytes.HasPrefix(ToUserNameElementBytes, msgToUserNameEndElementLiteral) { 587 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 588 return xml.Unmarshal(data, p) 589 } 590 ToUserNameElementBytes = ToUserNameElementBytes[len(msgToUserNameEndElementLiteral):] 591 592 // Encrypt 593 EncryptElementBytes := ToUserNameElementBytes 594 i = bytes.Index(EncryptElementBytes, msgEncryptStartElementLiteral) 595 if i == -1 { 596 EncryptElementBytes = data2 597 i = bytes.Index(EncryptElementBytes, msgEncryptStartElementLiteral) 598 if i == -1 { 599 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 600 return xml.Unmarshal(data, p) 601 } 602 } 603 EncryptElementBytes = EncryptElementBytes[i+len(msgEncryptStartElementLiteral):] 604 EncryptElementBytes = bytes.TrimLeftFunc(EncryptElementBytes, unicode.IsSpace) 605 if !bytes.HasPrefix(EncryptElementBytes, cdataStartLiteral) { 606 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 607 return xml.Unmarshal(data, p) 608 } 609 EncryptElementBytes = EncryptElementBytes[len(cdataStartLiteral):] 610 i = bytes.Index(EncryptElementBytes, cdataEndLiteral) 611 if i == -1 { 612 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 613 return xml.Unmarshal(data, p) 614 } 615 Encrypt := EncryptElementBytes[:i] 616 EncryptElementBytes = EncryptElementBytes[i+len(cdataEndLiteral):] 617 EncryptElementBytes = bytes.TrimLeftFunc(EncryptElementBytes, unicode.IsSpace) 618 if !bytes.HasPrefix(EncryptElementBytes, msgEncryptEndElementLiteral) { 619 log.Printf("[WARNING] xmlUnmarshal failed, data:\n%s\n", data) 620 return xml.Unmarshal(data, p) 621 } 622 623 p.ToUserName = string(ToUserName) 624 p.Base64EncryptedMsg = Encrypt 625 return nil 626 }