| 210 | }) |
| 211 | |
| 212 | func resourcePatchHandler(fileCache FileCache) handleFunc { |
| 213 | return withUser(func(_ http.ResponseWriter, r *http.Request, d *data) (int, error) { |
| 214 | src := r.URL.Path |
| 215 | dst := r.URL.Query().Get("destination") |
| 216 | action := r.URL.Query().Get("action") |
| 217 | dst, err := url.QueryUnescape(dst) |
| 218 | dst = path.Clean("/" + dst) |
| 219 | src = path.Clean("/" + src) |
| 220 | if !d.Check(src) || !d.Check(dst) { |
| 221 | return http.StatusForbidden, nil |
| 222 | } |
| 223 | if err != nil { |
| 224 | return errToStatus(err), err |
| 225 | } |
| 226 | if dst == "/" || src == "/" { |
| 227 | return http.StatusForbidden, nil |
| 228 | } |
| 229 | |
| 230 | err = checkParent(src, dst) |
| 231 | if err != nil { |
| 232 | return http.StatusBadRequest, err |
| 233 | } |
| 234 | |
| 235 | srcInfo, _ := d.user.Fs.Stat(src) |
| 236 | dstInfo, _ := d.user.Fs.Stat(dst) |
| 237 | same := os.SameFile(srcInfo, dstInfo) |
| 238 | |
| 239 | if action != "rename" || !same { |
| 240 | override := r.URL.Query().Get("override") == "true" |
| 241 | rename := r.URL.Query().Get("rename") == "true" |
| 242 | if !override && !rename { |
| 243 | if _, err = d.user.Fs.Stat(dst); err == nil { |
| 244 | return http.StatusConflict, nil |
| 245 | } |
| 246 | } |
| 247 | if rename { |
| 248 | dst = addVersionSuffix(dst, d.user.Fs) |
| 249 | } |
| 250 | |
| 251 | if override && !d.user.Perm.Modify { |
| 252 | return http.StatusForbidden, nil |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | err = d.RunHook(func() error { |
| 257 | return patchAction(r.Context(), action, src, dst, d, fileCache) |
| 258 | }, action, src, dst, d.user) |
| 259 | |
| 260 | return errToStatus(err), err |
| 261 | }) |
| 262 | } |
| 263 | |
| 264 | func checkParent(src, dst string) error { |
| 265 | rel, err := filepath.Rel(src, dst) |