Du kannst nicht mehr als 25 Themen auswählen Themen müssen entweder mit einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

288 Zeilen
7.3KB

  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. "reflect"
  13. "sort"
  14. "strings"
  15. "time"
  16. )
  17. //apiV1Main version 1 main entry for all wechat callbacks
  18. //
  19. func apiV1Main(w http.ResponseWriter, r *http.Request) {
  20. logRequestDebug(httputil.DumpRequest(r, true))
  21. if checkSignature(r) == false {
  22. log.Println("signature of URL incorrect")
  23. w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  24. w.WriteHeader(http.StatusUnauthorized)
  25. fmt.Fprintf(w, "") //empty string
  26. return
  27. }
  28. switch r.Method {
  29. case "POST":
  30. //answerWechatPostEcho(w, r)
  31. answerWechatPost(w, r)
  32. case "GET":
  33. answerInitialAuth(w, r)
  34. default:
  35. log.Println(fmt.Sprintf("FATAL: Unhandled HTTP %s", r.Method))
  36. response404Handler(w)
  37. //fmt.Fprintf(w, "Protocol Error: Expect GET or POST only")
  38. }
  39. }
  40. //
  41. //answerInitialAuth, when wechat first verify our URL for API hooks
  42. //
  43. func answerInitialAuth(w http.ResponseWriter, r *http.Request) {
  44. rq := r.URL.RawQuery
  45. m, _ := url.ParseQuery(rq)
  46. echostr, eok := m["echostr"]
  47. if checkSignature(r) && eok {
  48. fmt.Fprintf(w, echostr[0])
  49. } else {
  50. fmt.Fprintf(w, "wtf is wrong with the Internet")
  51. }
  52. }
  53. //
  54. //InWechatMsg what we received currently from wechat
  55. type InWechatMsg struct {
  56. header CommonHeader
  57. body interface{} //dynamic type
  58. }
  59. //
  60. //answerWechatPost distribute PostRequest according to xml body info
  61. func answerWechatPost(w http.ResponseWriter, r *http.Request) {
  62. in, valid := readWechatInput(r)
  63. //reply := "" //nothing
  64. if !valid {
  65. log.Println("Error: Invalid Input ")
  66. }
  67. openID := in.header.FromUserName
  68. if openID == "" {
  69. log.Println("nothing")
  70. }
  71. time.Sleep(5 * time.Second)
  72. v := reflect.ValueOf(answerWechatPost)
  73. log.Printf("Current Pointer: %d", v.Pointer())
  74. //debug.PrintStack()
  75. //load session into memory
  76. // session := startSession(openID, in, w, r) //who , input what?
  77. // session.ProcessInput()
  78. // //put session back to disk
  79. // session.Save()
  80. }
  81. // func (session) ProcessInput(w, r) {
  82. // //are we in an existing procedure
  83. // inProc, state := isInProc(openID) //if inside a procedure, resume last saved state
  84. // if inProc {
  85. // state = serveProc(state, in) //transit to new state
  86. // reply = state.response //xml response
  87. // } else {
  88. // state, processed := serveCommand(openID, in) //menu or txt command e.g. search
  89. // if !processed { // transfer to Customer Service (kf)
  90. // reply = buildKfForwardMsg(openID, "")
  91. // kfSendTxt(openID, "未识别的命令,已转接校友会理事会,稍后答复您")
  92. // } else {
  93. // reply = state.response
  94. // }
  95. // }
  96. // log.Println(reply) //instant reply, answering user's request
  97. // w.Header().Set("Content-Type", "text/xml; charset=utf-8")
  98. // fmt.Fprint(w, reply)
  99. // if !isEndingState(state) {
  100. // err := saveChatState(openID, state.Procedure, state)
  101. // if err != nil {
  102. // log.Println("Error Cannot Save chat sate")
  103. // log.Println(err)
  104. // log.Println(state)
  105. // }
  106. // } else { //state ending
  107. // cleanProcedure(openID, state.Procedure)
  108. // }
  109. // return
  110. // }
  111. func readWechatInput(r *http.Request) (result InWechatMsg, valid bool) {
  112. body, err := ioutil.ReadAll(r.Body)
  113. if err != nil {
  114. log.Println(err)
  115. valid = false
  116. return
  117. }
  118. d := decryptToXML(string(body))
  119. if d == "" {
  120. log.Println("Cannot decode Message : \r\n" + string(body))
  121. valid = false
  122. return
  123. }
  124. valid = true
  125. fmt.Printf("decrypt as: %s\n", d)
  126. result.header = ReadCommonHeader(d)
  127. switch result.header.MsgType {
  128. case "text":
  129. result.body = ReadTextMsg(d)
  130. case "image":
  131. result.body = ReadPicMsg(d)
  132. case "voice":
  133. result.body = ReadVoiceMsg(d)
  134. case "video":
  135. result.body = ReadVideoMsg(d)
  136. case "shortvideo":
  137. result.body = ReadShortVideoMsg(d)
  138. case "location":
  139. result.body = ReadLocationMsg(d)
  140. case "link":
  141. result.body = ReadLinkMsg(d)
  142. case "event":
  143. result.body = ReadEventMsg(d)
  144. default:
  145. log.Println("Fatal: unknown incoming message type" + result.header.MsgType)
  146. valid = false
  147. }
  148. return
  149. }
  150. func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) {
  151. body, _ := ioutil.ReadAll(r.Body)
  152. //fmt.Printf("get body: %s", string(body))
  153. s := ReadEncryptedMsg(string(body))
  154. //fmt.Printf("to decrypt %s", s.Encrypt)
  155. d := Decode(s.Encrypt)
  156. fmt.Printf("echo as: \r\n%s", d)
  157. e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2)
  158. f := strings.Replace(e, "FromUserName", "ToUserName", 2)
  159. g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2)
  160. fmt.Fprint(w, g)
  161. }
  162. //
  163. func checkSignature(r *http.Request) bool {
  164. rq := r.URL.RawQuery
  165. m, _ := url.ParseQuery(rq)
  166. signature, sok := m["signature"]
  167. timestamp, tok := m["timestamp"]
  168. nonce, nok := m["nonce"]
  169. token := APIConfig.Token
  170. if sok && tok && nok {
  171. //sort token, timestamp, nonce and join them
  172. strs := []string{token, timestamp[0], nonce[0]}
  173. sort.Strings(strs)
  174. s := strings.Join(strs, "")
  175. //calculate sha1
  176. h := sha1.New()
  177. h.Write([]byte(s))
  178. calculated := fmt.Sprintf("%x", h.Sum(nil))
  179. return signature[0] == calculated
  180. }
  181. return false
  182. }
  183. func checkSignature1() bool {
  184. s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c"
  185. t1 := "1492970761"
  186. n1 := "1850971833"
  187. token := APIConfig.Token
  188. strs := []string{token, t1, n1}
  189. sort.Strings(strs)
  190. s := strings.Join(strs, "")
  191. h := sha1.New()
  192. h.Write([]byte(s))
  193. us := fmt.Sprintf("%x", h.Sum(nil))
  194. return s1 == us
  195. }
  196. //webrootHandler sending contents to client when request "/"
  197. // essentially to prove the webserver is still alive
  198. // echo query string to the client
  199. func webrootHandler(w http.ResponseWriter, r *http.Request) {
  200. fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:])
  201. rq := r.URL.RawQuery
  202. m, _ := url.ParseQuery(rq)
  203. for index, element := range m {
  204. fmt.Fprintf(w, "\n%s => %s", index, element)
  205. }
  206. logRequestDebug(httputil.DumpRequest(r, true))
  207. }
  208. func logRequestDebug(data []byte, err error) {
  209. if err == nil {
  210. fmt.Printf("%s\n\n", string(data))
  211. } else {
  212. log.Fatalf("%s\n\n", err)
  213. }
  214. }
  215. //save uploaded 'file' into temporary location
  216. func uploadHandler(w http.ResponseWriter, r *http.Request) {
  217. r.ParseMultipartForm(32 << 20) //32MB memory
  218. file, handler, err := r.FormFile("file") //form-field 'file'
  219. if err != nil {
  220. fmt.Println(err)
  221. return
  222. }
  223. defer file.Close()
  224. fmt.Fprintf(w, "%v", handler.Header)
  225. f, err := os.OpenFile("/tmp/wechat_hitxy_"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
  226. if err != nil {
  227. fmt.Println(err)
  228. return
  229. }
  230. defer f.Close()
  231. io.Copy(f, file)
  232. }
  233. //when user requst an attachment from our server, we get the file from CRM server and reply it back
  234. //in this way, user has no 'direct-access' to the CRM server.
  235. func crmAttachmentHandler(w http.ResponseWriter, r *http.Request) {
  236. fileID := getCrmFileID(r.URL.Path)
  237. saveAs := GlobalPath.CRMAttachment + "crm_attach_" + fileID //add prefix for easy deleting
  238. if isFileExist(saveAs) {
  239. http.ServeFile(w, r, saveAs)
  240. return
  241. }
  242. if fileID == "" {
  243. log.Println("invalid fileID")
  244. response404Handler(w)
  245. return
  246. }
  247. log.Printf("download %s => %s", fileID, saveAs)
  248. crmDownloadAttachmentAs(fileID, saveAs)
  249. if !isFileExist(saveAs) {
  250. response404Handler(w)
  251. return
  252. }
  253. http.ServeFile(w, r, saveAs)
  254. }
  255. func getCrmFileID(urlPath string) string {
  256. return strings.TrimPrefix(urlPath, "/crmfiles/")
  257. }
  258. func response404Handler(w http.ResponseWriter) {
  259. w.WriteHeader(http.StatusNotFound)
  260. fmt.Fprintf(w, "not found")
  261. }