| @@ -5,6 +5,12 @@ import ( | |||
| "encoding/xml" | |||
| ) | |||
| //EncryptedMsg encrypted message from Wechat | |||
| type EncryptedMsg struct { | |||
| ToUserName string | |||
| Encrypt string | |||
| } | |||
| //all xml message has these headers | |||
| type CommonHeader struct { | |||
| ToUserName string | |||
| @@ -119,3 +125,10 @@ func ReadLinkMsg(s string) LinkMsg { | |||
| xml.Unmarshal([]byte(s), &r) | |||
| return r | |||
| } | |||
| //ReadEncryptedMsg get Encrypted Msg | |||
| func ReadEncryptedMsg(s string) EncryptedMsg { | |||
| var r = EncryptedMsg{} | |||
| xml.Unmarshal([]byte(s), &r) | |||
| return r | |||
| } | |||
| @@ -166,3 +166,13 @@ func TestLinkMsg(t *testing.T) { | |||
| AssertEqual(t, m.Url, "url", "Url failed") | |||
| AssertEqual(t, m.MsgId, int64(1234567890123456), "MsgId not match") | |||
| } | |||
| func TestReadEncryptMsg(t *testing.T) { | |||
| msg := `<xml> | |||
| <ToUserName><![CDATA[gh_f09231355c68]]></ToUserName> | |||
| <Encrypt><![CDATA[Dv3epMMhmmGU1o6lg71IfbpRrOYX1S8oZX3nwW0uBAHHMKx62T4KniS4efuf8fNHWf6gsF/YGaDraF6HhGOdKp8vbzluiIEsCnIveKN1pO+IUDOBBxzPAzQSFSYJ3OwVXWmBdBcC1S5guQrOxLysH+6UIWSor9cEef+94UAKTNw/MLB0zPfqK5TVoN1A0yobmP9OU8wtFJP0L1aKySPFGGbqBMfJkStRTrYLjIQfZ7pAIisB/g3c87w26r7LUz9hVh4ey3/T6cjQ8vKvgNKL3j8y4IwUdmnmTPrrdOsyA1pz69977xKHFtIptZYHKGD9dTW6PyPcKKTP6iOod6Agb8TI+is80auqHkjvUyvT/xPG8fxak/wI9BKzKndAnwxlcDG/8WElkHVl0TwxpsCb48ZxLEf4GFKaYaliC9xBVweKLNnqdbBmzwfe7GBNWC61h7KQYqwtZqMkZs3BBsStcQ==]]></Encrypt> | |||
| </xml>` | |||
| m := ReadEncryptedMsg(msg) | |||
| AssertEqual(t, m.ToUserName, "gh_f09231355c68", "ToUserName failed") | |||
| AssertEqual(t, m.Encrypt, "Dv3epMMhmmGU1o6lg71IfbpRrOYX1S8oZX3nwW0uBAHHMKx62T4KniS4efuf8fNHWf6gsF/YGaDraF6HhGOdKp8vbzluiIEsCnIveKN1pO+IUDOBBxzPAzQSFSYJ3OwVXWmBdBcC1S5guQrOxLysH+6UIWSor9cEef+94UAKTNw/MLB0zPfqK5TVoN1A0yobmP9OU8wtFJP0L1aKySPFGGbqBMfJkStRTrYLjIQfZ7pAIisB/g3c87w26r7LUz9hVh4ey3/T6cjQ8vKvgNKL3j8y4IwUdmnmTPrrdOsyA1pz69977xKHFtIptZYHKGD9dTW6PyPcKKTP6iOod6Agb8TI+is80auqHkjvUyvT/xPG8fxak/wI9BKzKndAnwxlcDG/8WElkHVl0TwxpsCb48ZxLEf4GFKaYaliC9xBVweKLNnqdbBmzwfe7GBNWC61h7KQYqwtZqMkZs3BBsStcQ==", "ToUserName failed") | |||
| } | |||
| @@ -3,6 +3,7 @@ package main | |||
| import ( | |||
| "crypto/sha1" | |||
| "fmt" | |||
| "io/ioutil" | |||
| "log" | |||
| "net/http" | |||
| "net/http/httputil" | |||
| @@ -37,7 +38,7 @@ func apiV1Main(w http.ResponseWriter, r *http.Request) { | |||
| case "GET": | |||
| answerInitialAuth(w, r) | |||
| default: | |||
| log.Fatalln("Unhandled HTTP %s", r.Method) | |||
| log.Fatalln(fmt.Sprintf("Unhandled HTTP %s", r.Method)) | |||
| fmt.Fprintf(w, "Protocol Error: Expect GET or POST only") | |||
| } | |||
| } | |||
| @@ -60,6 +61,15 @@ func answerInitialAuth(w http.ResponseWriter, r *http.Request) { | |||
| //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 | |||
| } | |||
| @@ -0,0 +1,55 @@ | |||
| package main | |||
| import ( | |||
| "crypto/sha1" | |||
| "fmt" | |||
| "math/rand" | |||
| "sort" | |||
| "strings" | |||
| "time" | |||
| ) | |||
| //BuildTextMsg Given a text message send it to wechat client | |||
| func BuildTextMsg(txt string, ToUserName string) (string, error) { | |||
| template := `<xml> | |||
| <ToUserName><![CDATA[%s]]></ToUserName> | |||
| <FromUserName><![CDATA[%s]]></FromUserName> | |||
| <CreateTime>%d</CreateTime> | |||
| <MsgType><![CDATA[text]]></MsgType> | |||
| <Content><![CDATA[%s]]></Content> | |||
| </xml>` | |||
| msg := fmt.Sprintf(template, ToUserName, "gh_f09231355c68", int32(time.Now().Unix()), txt) | |||
| fmt.Println(msg) | |||
| e := Encode(msg) | |||
| str, _, _, _ := signMsg(e) | |||
| fmt.Println(str) | |||
| return str, nil | |||
| } | |||
| func signMsg(content string) (xml string, timestamp int32, nonce int32, signature string) { | |||
| timestamp = int32(time.Now().Unix()) | |||
| nonce = rand.Int31() | |||
| strTimestamp := fmt.Sprintf("%d", timestamp) | |||
| strNonce := fmt.Sprintf("%d", nonce) | |||
| signature = getSignature(APIConfig.Token, strTimestamp, strNonce, content) | |||
| xml = "<xml>" + | |||
| "<Encrypt>" + content + "</Encrypt>" + | |||
| "<MsgSignature>" + signature + "</MsgSignature>" + | |||
| "<TimeStamp>" + strTimestamp + "</TimeStamp>" + | |||
| "<Nonce>" + strNonce + "</Nonce>" + | |||
| "</xml>" | |||
| return | |||
| } | |||
| func getSignature(token string, timestamp string, nonce string, content string) (signature string) { | |||
| strs := []string{token, timestamp, nonce, content} | |||
| sort.Strings(strs) | |||
| s := strings.Join(strs, "") | |||
| h := sha1.New() | |||
| h.Write([]byte(s)) | |||
| signature = fmt.Sprintf("%x", h.Sum(nil)) | |||
| return | |||
| } | |||
| @@ -0,0 +1,11 @@ | |||
| package main | |||
| import "testing" | |||
| func TestBuildTxtMsg(t *testing.T) { | |||
| BuildTextMsg("你好", "oUN420bxqFqlx0ZQHciUOesZO3PE") | |||
| } | |||
| func TextCalculateSignature (t *testing.T ){ | |||
| } | |||