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

173 lines
4.2KB

  1. package main
  2. import (
  3. "encoding/base64"
  4. "encoding/json"
  5. "errors"
  6. "fmt"
  7. "math/rand"
  8. "net/http"
  9. "net/url"
  10. "strconv"
  11. "strings"
  12. "time"
  13. )
  14. func crmpixelImage() (pixel []byte, err error) {
  15. b64pixel := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QcIBzEJ3JY/6wAAAAh0RVh0Q29tbWVudAD2zJa/AAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg=="
  16. pixel, err = base64.StdEncoding.DecodeString(b64pixel)
  17. return
  18. }
  19. func crmpixel(w http.ResponseWriter, r *http.Request) {
  20. cookie := crmpixelCookie(r)
  21. http.SetCookie(w, &cookie)
  22. //send out pixel 1x1 transparent png file
  23. pixel, err := crmpixelImage()
  24. if err == nil {
  25. w.Write(pixel)
  26. } else {
  27. fmt.Fprint(w, "decodig wrong")
  28. }
  29. }
  30. func crmpixelCookie(r *http.Request) (ret http.Cookie) {
  31. cookie, leadok := r.Cookie("biukop_cl") //crm lead
  32. if leadok != nil {
  33. ret, _ = createNewCookie(r)
  34. return
  35. }
  36. valid := isValidCookieBiukopCL(cookie.Value)
  37. if !valid {
  38. ret, _ = createNewCookie(r)
  39. return
  40. }
  41. //refresh expiration
  42. ret = *cookie
  43. ret.Expires = time.Now().Add(10 * 365 * 24 * time.Hour)
  44. return
  45. }
  46. func createNewCookie(r *http.Request) (ret http.Cookie, info crmdLead) {
  47. expiration := time.Now().Add(10 * 365 * 24 * time.Hour)
  48. info = crmCreateNewAnonymousLeadByHTTPRequest(r)
  49. cookieValue := buildBiukopCLValue(info.ID)
  50. ret = http.Cookie{Name: "biukop_cl", Value: cookieValue, Expires: expiration}
  51. return
  52. }
  53. func crmCreateNewAnonymousLeadByHTTPRequest(r *http.Request) (info crmdLead) {
  54. info.FirstName = "Anonymous"
  55. info.LastName = "User " + r.RemoteAddr
  56. info.Password = "Password"
  57. info.Status = "Anonymous"
  58. info.Description = "Anonymous user from " + r.RemoteAddr
  59. info.ForceDuplicate = true
  60. b, err := json.Marshal(info)
  61. if err != nil {
  62. return
  63. }
  64. entity, err := crmCreateEntity("Lead", b)
  65. if err == nil || isErrIndicateDuplicate(err) {
  66. info, _ = entity.(crmdLead)
  67. }
  68. return
  69. }
  70. func isValidCookieBiukopCL(cookieValue string) (valid bool) {
  71. valid = false
  72. //correctly split string
  73. s := strings.Split(cookieValue, "|")
  74. if len(s) != 4 || s[0] == "" {
  75. return
  76. }
  77. id, nonce, timestamp, signature := s[0], s[1], s[2], s[3]
  78. //check signature
  79. combined := id + nonce
  80. expected := calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete)
  81. if expected != signature {
  82. return
  83. }
  84. ts, err := strconv.Atoi(timestamp)
  85. if err != nil {
  86. return
  87. }
  88. if timestampOldThan(int32(ts), 86400) { //older than 1 day
  89. //find lead data
  90. _, err := crmpixelLead(id)
  91. if err != nil {
  92. return
  93. }
  94. }
  95. valid = true
  96. return
  97. }
  98. func buildBiukopCLsignature(id, nonce string) (timestamp, signature string) {
  99. combined := id + nonce
  100. timestamp = fmt.Sprintf("%d", time.Now().Unix())
  101. signature = calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete)
  102. return
  103. }
  104. func buildBiukopCLValue(id string) (ret string) {
  105. rand.Seed(time.Now().Unix())
  106. nonce := fmt.Sprintf("%d", rand.Intn(655352017))
  107. timestamp, signature := buildBiukopCLsignature(id, nonce)
  108. strs := []string{id, nonce, timestamp, signature} //order is very important
  109. ret = strings.Join(strs, "|")
  110. return
  111. }
  112. func crmpixelLead(id string) (info crmdLead, err error) {
  113. entity, err := crmGetEntity("Lead", id)
  114. if err != nil {
  115. return
  116. }
  117. info, ok := entity.(crmdLead)
  118. if !ok {
  119. err = errors.New("search lead, return a bad entity type")
  120. }
  121. return
  122. }
  123. //for user's initial registration, especially for wechat users
  124. //they visit a url that is specifically designed for them to
  125. //auth and input their profile data.
  126. //the url's query string will contains a token and a signature
  127. //so that it's verified, by single get request, to allow people to
  128. //enter their details into the CRM system.
  129. //
  130. //this handler, check's the query sting ,set an auth cookie to the client
  131. //and serve angular app, through an URL "/profile/edit"
  132. //or if the user has already been registered,
  133. //redirect user to a URL "/pages/dashboard"
  134. //
  135. func setTrackingCookieAndRecirect(w http.ResponseWriter, r *http.Request) {
  136. cookie := crmpixelCookie(r)
  137. http.SetCookie(w, &cookie)
  138. rq := r.URL.RawQuery
  139. m, _ := url.ParseQuery(rq)
  140. url, ok := m["url"]
  141. if ok {
  142. http.Redirect(w, r, url[0], 307) //302 temp redirect
  143. return
  144. }
  145. w.WriteHeader(http.StatusNotFound)
  146. fmt.Fprintf(w, "Not Found URL")
  147. }