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.

137 lines
3.0KB

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