Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

251 line
6.2KB

  1. package main
  2. import (
  3. "crypto/sha1"
  4. "fmt"
  5. "io"
  6. "io/ioutil"
  7. "log"
  8. "net/http"
  9. "net/http/httputil"
  10. "net/url"
  11. "os"
  12. "sort"
  13. "strings"
  14. )
  15. //apiV1Main version 1 main entry for all wechat callbacks
  16. //
  17. func apiV1Main(w http.ResponseWriter, r *http.Request) {
  18. logRequestDebug(httputil.DumpRequest(r, true))
  19. if checkSignature(r) == false {
  20. log.Println("signature of URL incorrect")
  21. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  22. w.WriteHeader(http.StatusUnauthorized)
  23. fmt.Fprintf(w, "") //empty string
  24. return
  25. }
  26. switch r.Method {
  27. case "POST":
  28. //answerWechatPostEcho(w, r)
  29. answerWechatPost(w, r)
  30. case "GET":
  31. answerInitialAuth(w, r)
  32. default:
  33. log.Println(fmt.Sprintf("FATAL: Unhandled HTTP %s", r.Method))
  34. response404Handler(w)
  35. //fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
  36. }
  37. }
  38. //
  39. //answerInitialAuth, when wechat first verify our URL for API hooks
  40. //
  41. func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
  42. rq := r.URL.RawQuery
  43. m, _ := url.ParseQuery(rq)
  44. echostr, eok := m["echostr"]
  45. if checkSignature(r) && eok {
  46. fmt.Fprintf(w, echostr[0])
  47. } else {
  48. fmt.Fprintf(w, "wtf is wrong with the Internet")
  49. }
  50. }
  51. //
  52. //answerWechatPost distribute PostRequest according to xml body info
  53. func answerWechatPost(w http.ResponseWriter, r *http.Request) {
  54. in, valid := readWechatInput(r)
  55. reply := "" //nothing
  56. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  57. if !valid {
  58. log.Println("Error: Invalid Input ")
  59. } else {
  60. //put into user session based pipeline
  61. AllInMessage <- in
  62. }
  63. //read instant response
  64. reply = <-in.instantResponse
  65. in.destroy()
  66. //uncomment the followin two lines to enable echo test
  67. // state, _ := echoCommand(in.header.FromUserName, in)
  68. // reply = state.response
  69. fmt.Fprint(w, reply)
  70. }
  71. func readWechatInput(r *http.Request) (result InWechatMsg, valid bool) {
  72. body, err := ioutil.ReadAll(r.Body)
  73. if err != nil {
  74. log.Println(err)
  75. valid = false
  76. return
  77. }
  78. d := decryptToXML(string(body))
  79. if d == "" {
  80. log.Println("Cannot decode Message : \r\n" + string(body))
  81. valid = false
  82. return
  83. }
  84. valid = true
  85. fmt.Printf("decrypt as: %s\n", d)
  86. result.init()
  87. result.header = ReadCommonHeader(d)
  88. switch result.header.MsgType {
  89. case "text":
  90. result.body = ReadTextMsg(d)
  91. case "image":
  92. result.body = ReadPicMsg(d)
  93. case "voice":
  94. result.body = ReadVoiceMsg(d)
  95. case "video":
  96. result.body = ReadVideoMsg(d)
  97. case "shortvideo":
  98. result.body = ReadShortVideoMsg(d)
  99. case "location":
  100. result.body = ReadLocationMsg(d)
  101. case "link":
  102. result.body = ReadLinkMsg(d)
  103. case "event":
  104. result.body = ReadEventMsg(d)
  105. default:
  106. log.Println("Fatal: unknown incoming message type" + result.header.MsgType)
  107. valid = false
  108. }
  109. return
  110. }
  111. func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) {
  112. body, _ := ioutil.ReadAll(r.Body)
  113. //fmt.Printf("get body: %s", string(body))
  114. s := ReadEncryptedMsg(string(body))
  115. //fmt.Printf("to decrypt %s", s.Encrypt)
  116. d := Decode(s.Encrypt)
  117. fmt.Printf("echo as: \r\n%s", d)
  118. e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2)
  119. f := strings.Replace(e, "FromUserName", "ToUserName", 2)
  120. g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2)
  121. fmt.Fprint(w, g)
  122. }
  123. //
  124. func checkSignature(r *http.Request) bool {
  125. rq := r.URL.RawQuery
  126. m, _ := url.ParseQuery(rq)
  127. signature, sok := m["signature"]
  128. timestamp, tok := m["timestamp"]
  129. nonce, nok := m["nonce"]
  130. token := APIConfig.Token
  131. if sok && tok && nok {
  132. return verifySignature(signature[0], timestamp[0], nonce[0], token)
  133. }
  134. return false
  135. }
  136. func verifySignature(signature, timestamp, nonce, token string) bool {
  137. //sort token, timestamp, nonce and join them
  138. strs := []string{token, timestamp, nonce}
  139. sort.Strings(strs)
  140. s := strings.Join(strs, "")
  141. //calculate sha1
  142. h := sha1.New()
  143. h.Write([]byte(s))
  144. calculated := fmt.Sprintf("%x", h.Sum(nil))
  145. return signature == calculated
  146. }
  147. // func checkSignature1() bool {
  148. // s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
  149. // t1 := "1492970761"
  150. // n1 := "1850971833"
  151. // token := APIConfig.Token
  152. // strs := []string{token, t1, n1}
  153. // sort.Strings(strs)
  154. // s := strings.Join(strs, "")
  155. // h := sha1.New()
  156. // h.Write([]byte(s))
  157. // us := fmt.Sprintf("%x", h.Sum(nil))
  158. // return s1 == us
  159. // }
  160. //webrootHandler sending contents to client when request "/"
  161. // essentially to prove the webserver is still alive
  162. // echo query string to the client
  163. func webrootHandler(w http.ResponseWriter, r *http.Request) {
  164. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  165. rq := r.URL.RawQuery
  166. m, _ := url.ParseQuery(rq)
  167. for index, element := range m {
  168. fmt.Fprintf(w, "\n%s => %s", index, element)
  169. }
  170. logRequestDebug(httputil.DumpRequest(r, true))
  171. }
  172. func logRequestDebug(data []byte, err error) {
  173. if err == nil {
  174. fmt.Printf("%s\n\n", string(data))
  175. } else {
  176. log.Fatalf("%s\n\n", err)
  177. }
  178. }
  179. //save uploaded 'file' into temporary location
  180. func uploadHandler(w http.ResponseWriter, r *http.Request) {
  181. r.ParseMultipartForm(32 << 20) //32MB memory
  182. file, handler, err := r.FormFile("file") //form-field 'file'
  183. if err != nil {
  184. fmt.Println(err)
  185. return
  186. }
  187. defer file.Close()
  188. fmt.Fprintf(w, "%v", handler.Header)
  189. f, err := os.OpenFile("/tmp/wechat_hitxy_"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
  190. if err != nil {
  191. fmt.Println(err)
  192. return
  193. }
  194. defer f.Close()
  195. io.Copy(f, file)
  196. }
  197. //when user requst an attachment from our server, we get the file from CRM server and reply it back
  198. //in this way, user has no 'direct-access' to the CRM server.
  199. func crmAttachmentHandler(w http.ResponseWriter, r *http.Request) {
  200. fileID := getCrmFileID(r.URL.Path)
  201. saveAs := CRMConfig.AttachmentCache + "crm_attach_" + fileID //add prefix for easy deleting
  202. if isFileExist(saveAs) {
  203. http.ServeFile(w, r, saveAs)
  204. return
  205. }
  206. if fileID == "" {
  207. log.Println("invalid fileID")
  208. response404Handler(w)
  209. return
  210. }
  211. log.Printf("download %s => %s", fileID, saveAs)
  212. crmDownloadAttachmentAs(fileID, saveAs)
  213. if !isFileExist(saveAs) {
  214. response404Handler(w)
  215. return
  216. }
  217. http.ServeFile(w, r, saveAs)
  218. }
  219. func getCrmFileID(urlPath string) string {
  220. return strings.TrimPrefix(urlPath, "/crmfiles/")
  221. }
  222. func response404Handler(w http.ResponseWriter) {
  223. w.WriteHeader(http.StatusNotFound)
  224. fmt.Fprintf(w, "not found")
  225. }