| @@ -1,15 +1,8 @@ | |||
| package main | |||
| import ( | |||
| "crypto/sha1" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "log" | |||
| "net/http" | |||
| "net/http/httputil" | |||
| "net/url" | |||
| "sort" | |||
| "strings" | |||
| ) | |||
| func main() { | |||
| @@ -26,111 +19,3 @@ func main() { | |||
| //CreateDefaultMenu() | |||
| http.ListenAndServe(":65500", nil) | |||
| } | |||
| //apiV1Main version 1 main entry for all wechat callbacks | |||
| // | |||
| func apiV1Main(w http.ResponseWriter, r *http.Request) { | |||
| logRequestDebug(httputil.DumpRequest(r, true)) | |||
| switch r.Method { | |||
| case "POST": | |||
| 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) | |||
| fmt.Printf("get body: %s", string(body)) | |||
| s := ReadEncryptedMsg(string(body)) | |||
| //fmt.Printf("to decrypt %s", s.Encrypt) | |||
| d := Decode(s.Encrypt) | |||
| fmt.Printf("decrypt as: %s", d) | |||
| h := ReadCommonHeader(d) | |||
| reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) | |||
| fmt.Fprint(w, reply) | |||
| return | |||
| } | |||
| // | |||
| 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, "\r\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) | |||
| } | |||
| } | |||
| @@ -0,0 +1,121 @@ | |||
| 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)) | |||
| switch r.Method { | |||
| case "POST": | |||
| 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) | |||
| fmt.Printf("get body: %s", string(body)) | |||
| s := ReadEncryptedMsg(string(body)) | |||
| //fmt.Printf("to decrypt %s", s.Encrypt) | |||
| d := Decode(s.Encrypt) | |||
| fmt.Printf("decrypt as: %s", d) | |||
| h := ReadCommonHeader(d) | |||
| reply, _ := BuildTextMsg(h.MsgType, h.FromUserName) | |||
| fmt.Fprint(w, reply) | |||
| return | |||
| } | |||
| // | |||
| 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, "\r\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) | |||
| } | |||
| } | |||
| @@ -0,0 +1,42 @@ | |||
| package main | |||
| import ( | |||
| "net/http" | |||
| "net/http/httptest" | |||
| "testing" | |||
| ) | |||
| //when we setup wechate parameters,we chat will verify us | |||
| func TestInitialSetup(t *testing.T) { | |||
| SetupConfig() | |||
| } | |||
| func TestWebRootHandler(t *testing.T) { | |||
| // Create a request to pass to our handler. We don't have any query parameters for now, so we'll | |||
| // pass 'nil' as the third parameter. | |||
| req, err := http.NewRequest("GET", "/dummydir", nil) | |||
| if err != nil { | |||
| t.Fatal(err) | |||
| } | |||
| // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response. | |||
| rr := httptest.NewRecorder() | |||
| handler := http.HandlerFunc(webrootHandler) | |||
| // Our handlers satisfy http.Handler, so we can call their ServeHTTP method | |||
| // directly and pass in our Request and ResponseRecorder. | |||
| handler.ServeHTTP(rr, req) | |||
| // Check the status code is what we expect. | |||
| if status := rr.Code; status != http.StatusOK { | |||
| t.Errorf("handler returned wrong status code: got %v want %v", | |||
| status, http.StatusOK) | |||
| } | |||
| // Check the response body is what we expect. | |||
| expected := `Hi there, I love dummydir!` | |||
| if rr.Body.String() != expected { | |||
| t.Errorf("handler returned unexpected body: got %v want %v", | |||
| rr.Body.String(), expected) | |||
| } | |||
| } | |||