Example #1
0
// httpList provides users with an endpoint to update their subscriptions.
// Depending on the HTTP method passed, different functionality will occur.
func httpList(e *env, header http.Header) interface{} {
	c := e.Context
	now := time.Now()
	id := e.Req.FormValue("id")
	noneMatch := e.Req.Header.Get("If-None-Match")
	var reqAtHead bool

	// Validate lkey; on PUT or DELETE we're modifying our subscription so we
	// must have a complete key. GET or POST may have either.
	if e.Req.Method == "PUT" || e.Req.Method == "DELETE" {
		if id == "" {
			return http.StatusBadRequest
		}
	} else if e.Req.Method != "POST" && e.Req.Method != "GET" {
		panic("Unknown/unsupported HTTP method: " + e.Req.Method)
	}

	var list *model.List
	var user *model.User

	// Perform a XG-transaction on our User and List, to keep them in sync.
	err := datastore.RunInTransaction(c, func(c appengine.Context) (err error) {
		var ukey *datastore.Key
		ukey, user, err = loadOrCreateUser(c, e.User)
		if err != nil {
			return err
		}
		if id != "" {
			data := (e.Req.Method == "GET" || e.Req.Method == "POST")
			list, err = app_model.Get(c, id, data)
			if err != nil {
				return err
			}
			if noneMatch != "" {
				state := list.State()
				reqAtHead = (state != "" && noneMatch == state)
			}
		}

		switch e.Req.Method {
		default:
			// If we're a GET, don't do anything.
			return nil
		case "POST":
			if list == nil {
				list, err = app_model.Create(c)
				user.Accept(list)
				c.Infof("create list=%s", list.Id)
			} else {
				c.Infof("update list=%s", list.Id)
			}

			var update model.EntryMap
			err = json.Unmarshal(e.Body, &update)
			if err != nil {
				return err
			}
			list.Data.UpdateWith(update)
			c.Infof("updating with EntryMap=%s", update)
		case "PUT":
			user.Accept(list)
			c.Infof("join list=%s", list.Id)
		case "DELETE":
			user.Refuse(list)
			c.Infof("part list=%s", list.Id)
		}
		list.Version += 1
		list.Updated = now
		// TODO: update Nonce. Possibly only on content changes.

		_, err = datastore.Put(c, ukey, user)
		if err == nil {
			err = app_model.Put(c, list)
		}
		return err
	}, &datastore.TransactionOptions{XG: true})

	// We're a GET without an ID; return the state of all subscribed lists.
	if err == nil && list == nil {
		if e.Req.Method != "GET" {
			panic("Must have List key unless calling with GET")
		}
		out, err := listState(c, user)
		if err == nil {
			return out
		}
	}
	if err != nil {
		if err == datastore.ErrNoSuchEntity {
			return http.StatusNotFound
		}
		return err
	}

	// Render the complete state of the list.
	if state := list.State(); state != "" {
		header.Set("ETag", state)
	}
	header.Set("Last-Modified", list.Updated.Format(time.RFC1123Z))
	header.Set("X-List-Id", list.Id)
	for _, v := range list.Data {
		// Note that these are case-insensitive: this is fine, since all generated IDs are case-insensitive.
		if v.Hint != "" {
			// TODO: is \u0000 always going to be 1: ?
			header.Set("X-Entry-"+v.Id, v.Hint[1:])
		}
	}
	if reqAtHead {
		// TODO: If we're a POST, this status code doesn't make a whole lot of sense.
		return http.StatusNotModified
	}
	return list.Data
}
Example #2
0
// 生成 退订 邮件的 token
func GenUnsubscribeToken(user *model.User) string {
	return util.Md5(user.String() + Config["unsubscribe_token_key"])
}
Example #3
0
// 生成 退订 邮件的 token
func (EmailLogic) GenUnsubscribeToken(user *model.User) string {
	return goutils.Md5(user.String() + config.ConfigFile.MustValue("security", "unsubscribe_token_key"))
}