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.

138 line
3.3KB

  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. answerWechatPost(w, r)
  27. case "GET":
  28. answerInitialAuth(w, r)
  29. default:
  30. log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method))
  31. fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
  32. }
  33. }
  34. //
  35. //answerInitialAuth, when wechat first verify our URL for API hooks
  36. //
  37. func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
  38. rq := r.URL.RawQuery
  39. m, _ := url.ParseQuery(rq)
  40. echostr, eok := m["echostr"]
  41. if checkSignature(r) && eok {
  42. fmt.Fprintf(w, echostr[0])
  43. } else {
  44. fmt.Fprintf(w, "wtf is wrong with the Internet")
  45. }
  46. }
  47. //answerWechatPost distribute PostRequest according to xml body info
  48. //
  49. func answerWechatPost(w http.ResponseWriter, r *http.Request) {
  50. body, _ := ioutil.ReadAll(r.Body)
  51. //fmt.Printf("get body: %s", string(body))
  52. s := ReadEncryptedMsg(string(body))
  53. //fmt.Printf("to decrypt %s", s.Encrypt)
  54. d := Decode(s.Encrypt)
  55. fmt.Printf("decrypt as: %s", d)
  56. h := ReadCommonHeader(d)
  57. reply, _ := BuildTextMsg(h.MsgType, h.FromUserName)
  58. if h.MsgType == "voice" {
  59. a := ReadVoiceMsg(d)
  60. reply, _ = BuildTextMsg(a.Recognition, h.FromUserName)
  61. }
  62. if h.MsgType == "event" {
  63. a := ReadEventMsg(d)
  64. reply, _ = BuildTextMsg(a.Event+"/"+a.EventKey, h.FromUserName)
  65. }
  66. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  67. fmt.Fprint(w, reply)
  68. return
  69. }
  70. //
  71. func checkSignature(r *http.Request) bool {
  72. rq := r.URL.RawQuery
  73. m, _ := url.ParseQuery(rq)
  74. signature, sok := m["signature"]
  75. timestamp, tok := m["timestamp"]
  76. nonce, nok := m["nonce"]
  77. token := APIConfig.Token
  78. if sok && tok && nok {
  79. //sort token, timestamp, nonce and join them
  80. strs := []string{token, timestamp[0], nonce[0]}
  81. sort.Strings(strs)
  82. s := strings.Join(strs, "")
  83. //calculate sha1
  84. h := sha1.New()
  85. h.Write([]byte(s))
  86. calculated := fmt.Sprintf("%x", h.Sum(nil))
  87. return signature[0] == calculated
  88. }
  89. return false
  90. }
  91. func checkSignature1() bool {
  92. s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
  93. t1 := "1492970761"
  94. n1 := "1850971833"
  95. token := APIConfig.Token
  96. strs := []string{token, t1, n1}
  97. sort.Strings(strs)
  98. s := strings.Join(strs, "")
  99. h := sha1.New()
  100. h.Write([]byte(s))
  101. us := fmt.Sprintf("%x", h.Sum(nil))
  102. return s1 == us
  103. }
  104. //webrootHandler sending contents to client when request "/"
  105. // essentially to prove the webserver is still alive
  106. // echo query string to the client
  107. func webrootHandler(w http.ResponseWriter, r *http.Request) {
  108. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  109. rq := r.URL.RawQuery
  110. m, _ := url.ParseQuery(rq)
  111. for index, element := range m {
  112. fmt.Fprintf(w, "\n%s => %s", index, element)
  113. }
  114. logRequestDebug(httputil.DumpRequest(r, true))
  115. }
  116. func logRequestDebug(data []byte, err error) {
  117. if err == nil {
  118. fmt.Printf("%s\n\n", data)
  119. } else {
  120. log.Fatalf("%s\n\n", err)
  121. }
  122. }