package main import ( "crypto/sha1" "fmt" "io/ioutil" "log" "net/http" "net/http/httputil" "net/url" "sort" "strings" ) //apiV1Main version 1 main entry for all wechat callbacks // func apiV1Main(w http.ResponseWriter, r *http.Request) { logRequestDebug(httputil.DumpRequest(r, true)) if checkSignature(r) == false { log.Println("signature of URL incorrect") w.Header().Set("Content-Type", "text/xml; charset=utf-8") w.WriteHeader(http.StatusUnauthorized) fmt.Fprintf(w, "") //empty string return } switch r.Method { case "POST": //answerWechatPostEcho(w, r) answerWechatPost(w, r) case "GET": answerInitialAuth(w, r) default: log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method)) fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") } } // //answerInitialAuth, when wechat first verify our URL for API hooks // func answerInitialAuth(w http.ResponseWriter, r *http.Request) { rq := r.URL.RawQuery m, _ := url.ParseQuery(rq) echostr, eok := m["echostr"] if checkSignature(r) && eok { fmt.Fprintf(w, echostr[0]) } else { fmt.Fprintf(w, "wtf is wrong with the Internet") } } //answerWechatPost distribute PostRequest according to xml body info // func answerWechatPost(w http.ResponseWriter, r *http.Request) { body, _ := ioutil.ReadAll(r.Body) d := decryptToXML(string(body)) fmt.Printf("decrypt as: %s\n", d) h := ReadCommonHeader(d) reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) if h.MsgType == "voice" { a := ReadVoiceMsg(d) reply, _ = BuildTextMsg(a.Recognition, h.FromUserName) } if h.MsgType == "event" { a := ReadEventMsg(d) if a.Event == "LOCATION" { reply = BuildLocationMsg(0, 0, 0, h.FromUserName) fmt.Printf("output %s", reply) } else { reply, _ = BuildTextMsg(a.Event+"/"+a.EventKey, h.FromUserName) } mediaID := "cU8BYvAEp3H25V-yGO3WBtMVk2bZcEBgf_kje7V-EPkRA_U4x-OAWb_ONg6Y-Qxt" //video //mediaID := "e2iNEiSxCX5TV1WbFd0TQMqvVrqFDbDOacdjgQ-OAuE" //news reply = buildVideoMsg(h.FromUserName, mediaID, "标题", a.Event+"/"+a.EventKey) reply = buildUploadPicMsg(h.FromUserName, "media_for_test/640x480.jpg") reply = buildUploadVoiceMsg(h.FromUserName, "media_for_test/music.mp3") } w.Header().Set("Content-Type", "text/xml; charset=utf-8") fmt.Fprint(w, reply) return } func answerWechatPostEcho(w http.ResponseWriter, r *http.Request) { body, _ := ioutil.ReadAll(r.Body) //fmt.Printf("get body: %s", string(body)) s := ReadEncryptedMsg(string(body)) //fmt.Printf("to decrypt %s", s.Encrypt) d := Decode(s.Encrypt) fmt.Printf("echo as: \n%s", d) e := strings.Replace(d, "ToUserName", "FDDD20170506xyzunique", 2) f := strings.Replace(e, "FromUserName", "ToUserName", 2) g := strings.Replace(f, "FDDD20170506xyzunique", "FromUserName", 2) fmt.Fprint(w, g) } // func checkSignature(r *http.Request) bool { rq := r.URL.RawQuery m, _ := url.ParseQuery(rq) signature, sok := m["signature"] timestamp, tok := m["timestamp"] nonce, nok := m["nonce"] token := APIConfig.Token if sok && tok && nok { //sort token, timestamp, nonce and join them strs := []string{token, timestamp[0], nonce[0]} sort.Strings(strs) s := strings.Join(strs, "") //calculate sha1 h := sha1.New() h.Write([]byte(s)) calculated := fmt.Sprintf("%x", h.Sum(nil)) return signature[0] == calculated } return false } func checkSignature1() bool { s1 := "e39de9f2e28079c01ebb4b803dfc3442b819545c" t1 := "1492970761" n1 := "1850971833" token := APIConfig.Token strs := []string{token, t1, n1} sort.Strings(strs) s := strings.Join(strs, "") h := sha1.New() h.Write([]byte(s)) us := fmt.Sprintf("%x", h.Sum(nil)) return s1 == us } //webrootHandler sending contents to client when request "/" // essentially to prove the webserver is still alive // echo query string to the client func webrootHandler(w http.ResponseWriter, r *http.Request) { fmt.Fprintf(w, "Hi there, I love %s!", r.URL.Path[1:]) rq := r.URL.RawQuery m, _ := url.ParseQuery(rq) for index, element := range m { fmt.Fprintf(w, "\n%s => %s", index, element) } logRequestDebug(httputil.DumpRequest(r, true)) } func logRequestDebug(data []byte, err error) { if err == nil { fmt.Printf("%s\n\n", data) } else { log.Fatalf("%s\n\n", err) } }