package main import ( "encoding/base64" "encoding/json" "errors" "fmt" "log" "math/rand" "net/http" "net/url" "strconv" "strings" "time" ) var cookLeadID = "biukop_cl" func crmpixelImage() (pixel []byte, err error) { b64pixel := "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4QcIBzEJ3JY/6wAAAAh0RVh0Q29tbWVudAD2zJa/AAAADUlEQVQI12NgYGBgAAAABQABXvMqOgAAAABJRU5ErkJggg==" pixel, err = base64.StdEncoding.DecodeString(b64pixel) return } func crmpixel(w http.ResponseWriter, r *http.Request) { cookie := crmpixelCookie(r) http.SetCookie(w, &cookie) //send out pixel 1x1 transparent png file pixel, err := crmpixelImage() if err == nil { w.Write(pixel) } else { fmt.Fprint(w, "decodig pixel image wrong") } } func crmpixelCookie(r *http.Request) (ret http.Cookie) { cookie, leadok := r.Cookie(cookLeadID) //crm lead if leadok != nil { ret, _ = createNewCookie(r) return } valid := isValidCookieBiukopCL(cookie.Value) if !valid { ret, _ = createNewCookie(r) return } //refresh expiration ret = *cookie ret.Expires = time.Now().Add(10 * 365 * 24 * time.Hour) return } func getLeadIDFromCookie(r *http.Request) (leadID string, ok bool) { ok = false cookie, leadok := r.Cookie(cookLeadID) //crm lead if leadok != nil { return } valid := isValidCookieBiukopCL(cookie.Value) if !valid { return } //correctly split string s := strings.Split(cookie.Value, "|") if len(s) != 4 || s[0] == "" { return } ok = true leadID = s[0] return } func createNewCookie(r *http.Request) (ret http.Cookie, info crmdLead) { info = crmCreateNewAnonymousLeadByHTTPRequest(r) ret = createNewCookieByLeadID(info.ID) return } func createNewCookieByLeadID(leadID string) (ret http.Cookie) { expiration := time.Now().Add(10 * 365 * 24 * time.Hour) cookieValue := buildBiukopCLValue(leadID) ret = http.Cookie{Name: cookLeadID, Value: cookieValue, Expires: expiration} return } func crmCreateNewAnonymousLeadByHTTPRequest(r *http.Request) (info crmdLead) { info.FirstName = "Anonymous" info.LastName = "User " + r.RemoteAddr info.Password = "Password" info.Status = "Anonymous" info.Description = "Anonymous user from " + r.RemoteAddr info.ForceDuplicate = true b, err := json.Marshal(info) if err != nil { return } entity, err := crmCreateEntity("Lead", b) if err == nil || isErrIndicateDuplicate(err) { info, _ = entity.(crmdLead) } return } func isValidCookieBiukopCL(cookieValue string) (valid bool) { valid = false //correctly split string s := strings.Split(cookieValue, "|") if len(s) != 4 || s[0] == "" { return } id, nonce, timestamp, signature := s[0], s[1], s[2], s[3] //check signature combined := id + nonce expected := calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete) if expected != signature { return } ts, err := strconv.Atoi(timestamp) if err != nil { return } if timestampOldThan(int32(ts), 86400) { //older than 1 day //find lead data _, err := crmpixelLead(id) if err != nil { return } } valid = true return } func buildBiukopCLsignature(id, nonce string) (timestamp, signature string) { combined := id + nonce timestamp = fmt.Sprintf("%d", time.Now().Unix()) signature = calculateSignature(timestamp, combined, IntraAPIConfig.CRMSecrete) return } func buildBiukopCLValue(id string) (ret string) { rand.Seed(time.Now().Unix()) nonce := fmt.Sprintf("%d", rand.Intn(655352017)) timestamp, signature := buildBiukopCLsignature(id, nonce) strs := []string{id, nonce, timestamp, signature} //order is very important ret = strings.Join(strs, "|") return } func crmpixelLead(id string) (info crmdLead, err error) { entity, err := crmGetEntity("Lead", id) if err != nil { return } info, ok := entity.(crmdLead) if !ok { err = errors.New("search lead, return a bad entity type") } return } //for user's initial registration, especially for wechat users //they visit a url that is specifically designed for them to //auth and input their profile data. //the url's query string will contains a token and a signature //so that it's verified, by single get request, to allow people to //enter their details into the CRM system. // //this handler, check's the query sting ,set an auth cookie to the client //and serve angular app, through an URL "/profile/edit" //or if the user has already been registered, //redirect user to a URL "/pages/dashboard" // func setTrackingCookieAndRecirect(w http.ResponseWriter, r *http.Request) { rq := r.URL.RawQuery m, _ := url.ParseQuery(rq) //set cookie if any leadID, ok := m["lid"] if ok { log.Println("setlead cookie :" + leadID[0]) cookie := createNewCookieByLeadID(leadID[0]) http.SetCookie(w, &cookie) } else { cookie := crmpixelCookie(r) http.SetCookie(w, &cookie) } url, ok := m["url"] if ok { http.Redirect(w, r, url[0], 307) //302 temp redirect return } w.WriteHeader(http.StatusNotFound) fmt.Fprintf(w, "Not Found URL") }