You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

165 lines
4.4KB

  1. package main
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. "io/ioutil"
  6. "log"
  7. "net/http"
  8. "net/http/httputil"
  9. "net/url"
  10. "sort"
  11. "strings"
  12. )
  13. //apiV1Main version 1 main entry for all wechat callbacks
  14. //
  15. func apiV1Main(w http.ResponseWriter, r *http.Request) {
  16. logRequestDebug(httputil.DumpRequest(r, true))
  17. if checkSignature(r) == false {
  18. log.Println("signature of URL incorrect")
  19. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  20. w.WriteHeader(http.StatusUnauthorized)
  21. fmt.Fprintf(w, "") //empty string
  22. return
  23. }
  24. switch r.Method {
  25. case "POST":
  26. //answerWechatPostEcho(w, r)
  27. answerWechatPost(w, r)
  28. case "GET":
  29. answerInitialAuth(w, r)
  30. default:
  31. log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method))
  32. fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
  33. }
  34. }
  35. //
  36. //answerInitialAuth, when wechat first verify our URL for API hooks
  37. //
  38. func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
  39. rq := r.URL.RawQuery
  40. m, _ := url.ParseQuery(rq)
  41. echostr, eok := m["echostr"]
  42. if checkSignature(r) && eok {
  43. fmt.Fprintf(w, echostr[0])
  44. } else {
  45. fmt.Fprintf(w, "wtf is wrong with the Internet")
  46. }
  47. }
  48. //answerWechatPost distribute PostRequest according to xml body info
  49. //
  50. func answerWechatPost(w http.ResponseWriter, r *http.Request) {
  51. body, _ := ioutil.ReadAll(r.Body)
  52. d := decryptToXML(string(body))
  53. fmt.Printf("decrypt as: %s\n", d)
  54. h := ReadCommonHeader(d)
  55. reply, _ := BuildTextMsg(h.MsgType, h.FromUserName)
  56. if h.MsgType == "voice" {
  57. a := ReadVoiceMsg(d)
  58. reply, _ = BuildTextMsg(a.Recognition, h.FromUserName)
  59. }
  60. if h.MsgType == "event" {
  61. a := ReadEventMsg(d)
  62. if a.Event == "LOCATION" {
  63. reply = BuildLocationMsg(0, 0, 0, h.FromUserName)
  64. fmt.Printf("output %s", reply)
  65. } else {
  66. reply, _ = BuildTextMsg(a.Event+"/"+a.EventKey, h.FromUserName)
  67. }
  68. //mediaID := "cU8BYvAEp3H25V-yGO3WBtMVk2bZcEBgf_kje7V-EPkRA_U4x-OAWb_ONg6Y-Qxt" //video
  69. //mediaID := "e2iNEiSxCX5TV1WbFd0TQPTdMx8WbvpkOs_iNhSVQHY" // 236 second mp3
  70. //mediaID := "e2iNEiSxCX5TV1WbFd0TQDVyk970GxTcBWMnqc2RzF0" //5 sec mp3
  71. //mediaID := "e2iNEiSxCX5TV1WbFd0TQMqvVrqFDbDOacdjgQ-OAuE" //news
  72. //reply = buildVideoMsg(h.FromUserName, mediaID, "标题", a.Event+"/"+a.EventKey)
  73. //reply = buildUploadPicMsg(h.FromUserName, "media_for_test/640x480.jpg")
  74. //reply = buildUploadVoiceMsg(h.FromUserName, "media_for_test/music.mp3")
  75. //reply = buildVoiceMsg(h.FromUserName, mediaID)
  76. reply = buildSampleMusicMsg(h.FromUserName)
  77. }
  78. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  79. fmt.Fprint(w, reply)
  80. return
  81. }
  82. func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) {
  83. body, _ := ioutil.ReadAll(r.Body)
  84. //fmt.Printf("get body: %s", string(body))
  85. s := ReadEncryptedMsg(string(body))
  86. //fmt.Printf("to decrypt %s", s.Encrypt)
  87. d := Decode(s.Encrypt)
  88. fmt.Printf("echo as: \n%s", d)
  89. e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2)
  90. f := strings.Replace(e, "FromUserName", "ToUserName", 2)
  91. g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2)
  92. fmt.Fprint(w, g)
  93. }
  94. //
  95. func checkSignature(r *http.Request) bool {
  96. rq := r.URL.RawQuery
  97. m, _ := url.ParseQuery(rq)
  98. signature, sok := m["signature"]
  99. timestamp, tok := m["timestamp"]
  100. nonce, nok := m["nonce"]
  101. token := APIConfig.Token
  102. if sok && tok && nok {
  103. //sort token, timestamp, nonce and join them
  104. strs := []string{token, timestamp[0], nonce[0]}
  105. sort.Strings(strs)
  106. s := strings.Join(strs, "")
  107. //calculate sha1
  108. h := sha1.New()
  109. h.Write([]byte(s))
  110. calculated := fmt.Sprintf("%x", h.Sum(nil))
  111. return signature[0] == calculated
  112. }
  113. return false
  114. }
  115. func checkSignature1() bool {
  116. s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
  117. t1 := "1492970761"
  118. n1 := "1850971833"
  119. token := APIConfig.Token
  120. strs := []string{token, t1, n1}
  121. sort.Strings(strs)
  122. s := strings.Join(strs, "")
  123. h := sha1.New()
  124. h.Write([]byte(s))
  125. us := fmt.Sprintf("%x", h.Sum(nil))
  126. return s1 == us
  127. }
  128. //webrootHandler sending contents to client when request "/"
  129. // essentially to prove the webserver is still alive
  130. // echo query string to the client
  131. func webrootHandler(w http.ResponseWriter, r *http.Request) {
  132. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  133. rq := r.URL.RawQuery
  134. m, _ := url.ParseQuery(rq)
  135. for index, element := range m {
  136. fmt.Fprintf(w, "\n%s => %s", index, element)
  137. }
  138. logRequestDebug(httputil.DumpRequest(r, true))
  139. }
  140. func logRequestDebug(data []byte, err error) {
  141. if err == nil {
  142. fmt.Printf("%s\n\n", data)
  143. } else {
  144. log.Fatalf("%s\n\n", err)
  145. }
  146. }