// UpdateEntryWithEtag updates an entry in persistent storage in a way that // detects concurrent modification. It also prevents users from modifying // entries they do not own by returning ErrNoSuchId. t, the transaction, // must be non nil. func UpdateEntryWithEtag( store SafeUpdateEntryRunner, t db.Transaction, id int64, tag uint64, key *vsafe.Key, update functional.Filterer) error { if t == nil { panic("Transaction must be non-nil") } var origEntry vsafe.EntryWithEtag err := EntryByIdWithEtag(store, t, id, key, &origEntry) if err != nil { return err } err = update.Filter(&origEntry.Entry) if err == functional.Skipped { return nil } if err != nil { return err } if tag != origEntry.Etag { return ErrConcurrentModification } origEntry.Id = id return UpdateEntry(store, t, key, &origEntry.Entry) }
func (h *Handler) doPost(w http.ResponseWriter, r *http.Request, id int64) { var err error session := common.GetUserSession(r) if !common.VerifyXsrfToken(r, kSingle) { err = common.ErrXsrf } else if http_util.HasParam(r.Form, "delete") { if isIdValid(id) { err = h.Store.RemoveEntry(nil, id, session.User.GetOwner()) } } else if http_util.HasParam(r.Form, "cancel") { // Do nothing } else { var mutation functional.Filterer mutation, err = toEntry(r.Form) if err == nil { if isIdValid(id) { tag, _ := strconv.ParseUint(r.Form.Get("etag"), 10, 64) err = h.Doer.Do(func(t db.Transaction) error { return vsafedb.UpdateEntryWithEtag( h.Store, t, id, tag, session.Key(), mutation) }) } else { var newId int64 var entry vsafe.Entry mutation.Filter(&entry) newId, err = vsafedb.AddEntry(h.Store, nil, session.Key(), &entry) if err == nil { id = newId } } } } if err == vsafedb.ErrConcurrentModification { err = errors.New("Someone else updated this entry after you started. Click cancel and try again.") } if err != nil { http_util.WriteTemplate( w, kTemplate, newView( r.Form, isIdValid(id), session.Key().Id, common.NewXsrfToken(r, kSingle), err)) } else { goBack(w, r, id) } }