Nelze vybrat více než 25 témat Téma musí začínat písmenem nebo číslem, může obsahovat pomlčky („-“) a může být dlouhé až 35 znaků.

209 lines
4.9KB

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