Switch to latest upstream go-webdav

* Use one, fixed path layout
* New type `ConditionalMatch` for contents of `If-Match`/`If-None-Match`
  header
This commit is contained in:
Conrad Hoffmann 2022-11-22 16:05:00 +01:00
parent 7f0f9fd365
commit 68de660456
3 changed files with 53 additions and 37 deletions

View file

@ -84,7 +84,7 @@ func ensureLocalDir(path string) error {
// don't use this directly, use localCalDAVPath or localCardDAVPath instead.
func (b *filesystemBackend) safeLocalPath(homeSetPath string, urlPath string) (string, error) {
localPath := filepath.Join(b.path, homeSetPath)
localPath := filepath.Join(b.path, homeSetPath, "default")
if err := ensureLocalDir(localPath); err != nil {
return "", err
}
@ -96,10 +96,10 @@ func (b *filesystemBackend) safeLocalPath(homeSetPath string, urlPath string) (s
// We are mapping to local filesystem path, so be conservative about what to accept
// TODO this changes once multiple addess books are supported
dir, file := path.Split(urlPath)
// only accept resources in prefix, no subdirs for now
if dir != homeSetPath {
if strings.HasPrefix(dir, homeSetPath+"/") {
err := fmt.Errorf("invalid request path: %s", urlPath)
// only accept resources in default calendar/adress book for now
if path.Clean(dir) != path.Join(homeSetPath, "default") {
if strings.HasPrefix(dir, homeSetPath) {
err := fmt.Errorf("invalid request path: %s (%s is not %s)", urlPath, dir, path.Join(homeSetPath, "default"))
return "", webdav.NewHTTPError(400, err)
} else {
err := fmt.Errorf("Access to resource outside of home set: %s", urlPath)
@ -228,26 +228,27 @@ func createDefaultAddressBook(path, localPath string) error {
func (b *filesystemBackend) AddressBook(ctx context.Context) (*carddav.AddressBook, error) {
debug.Printf("filesystem.AddressBook()")
path, err := b.localCardDAVPath(ctx, "")
localPath, err := b.localCardDAVPath(ctx, "")
if err != nil {
return nil, err
}
path = filepath.Join(path, "addressbook.json")
localPath = filepath.Join(localPath, "addressbook.json")
debug.Printf("loading addressbook from %s", path)
debug.Printf("loading addressbook from %s", localPath)
data, readErr := ioutil.ReadFile(path)
data, readErr := ioutil.ReadFile(localPath)
if os.IsNotExist(readErr) {
urlPath, err := b.AddressbookHomeSetPath(ctx)
if err != nil {
return nil, err
}
debug.Printf("creating default addressbook (URL:path): %s:%s", urlPath, path)
err = createDefaultAddressBook(urlPath, path)
urlPath = path.Join(urlPath, "default") + "/"
debug.Printf("creating default addressbook (URL:path): %s:%s", urlPath, localPath)
err = createDefaultAddressBook(urlPath, localPath)
if err != nil {
return nil, err
}
data, readErr = ioutil.ReadFile(path)
data, readErr = ioutil.ReadFile(localPath)
}
if readErr != nil {
return nil, fmt.Errorf("error opening address book: %s", readErr.Error())
@ -330,7 +331,7 @@ func (b *filesystemBackend) loadAllContacts(ctx context.Context, propFilter []st
}
obj := carddav.AddressObject{
Path: path.Join(homeSetPath, filepath.Base(filename)),
Path: path.Join(homeSetPath, "default", filepath.Base(filename)),
ModTime: info.ModTime(),
ContentLength: info.Size(),
ETag: etag,
@ -375,7 +376,7 @@ func (b *filesystemBackend) loadAllCalendars(ctx context.Context, propFilter []s
}
obj := caldav.CalendarObject{
Path: path.Join(homeSetPath, filepath.Base(filename)),
Path: path.Join(homeSetPath, "default", filepath.Base(filename)),
ModTime: info.ModTime(),
ContentLength: info.Size(),
ETag: etag,
@ -427,17 +428,25 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string
}
flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC
if opts.IfNoneMatch {
// TODO handle IfNoneMatch == ETag
if opts.IfNoneMatch.IsWildcard() {
// Make sure we're not overwriting an existing file
flags |= os.O_EXCL
} else if opts.IfMatch == "*" {
} else if opts.IfMatch.IsWildcard() {
// Make sure we _are_ overwriting an existing file
flags &= ^os.O_CREATE
} else if opts.IfMatch != "" {
} else if opts.IfMatch.IsSet() {
// Make sure we overwrite the _right_ file
etag, err := etagForFile(localPath)
if err != nil {
return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
}
if opts.IfMatch != etag {
err = fmt.Errorf("If-Match does not match current ETag (%s/%s)", opts.IfMatch, etag)
want, err := opts.IfMatch.ETag()
if err != nil {
return "", webdav.NewHTTPError(http.StatusBadRequest, err)
}
if want != etag {
err = fmt.Errorf("If-Match does not match current ETag (%s/%s)", want, etag)
return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
}
}
@ -495,26 +504,27 @@ func createDefaultCalendar(path, localPath string) error {
func (b *filesystemBackend) Calendar(ctx context.Context) (*caldav.Calendar, error) {
debug.Printf("filesystem.Calendar()")
path, err := b.localCalDAVPath(ctx, "")
localPath, err := b.localCalDAVPath(ctx, "")
if err != nil {
return nil, err
}
path = filepath.Join(path, "calendar.json")
localPath = filepath.Join(localPath, "calendar.json")
debug.Printf("loading calendar from %s", path)
debug.Printf("loading calendar from %s", localPath)
data, readErr := ioutil.ReadFile(path)
data, readErr := ioutil.ReadFile(localPath)
if os.IsNotExist(readErr) {
homeSetPath, err := b.CalendarHomeSetPath(ctx)
urlPath, err := b.CalendarHomeSetPath(ctx)
if err != nil {
return nil, err
}
debug.Printf("creating default calendar (URL:path): %s:%s", homeSetPath, path)
err = createDefaultCalendar(homeSetPath, path)
urlPath = path.Join(urlPath, "default") + "/"
debug.Printf("creating default calendar (URL:path): %s:%s", urlPath, localPath)
err = createDefaultCalendar(urlPath, localPath)
if err != nil {
return nil, err
}
data, readErr = ioutil.ReadFile(path)
data, readErr = ioutil.ReadFile(localPath)
}
if readErr != nil {
return nil, fmt.Errorf("error opening calendar: %s", readErr.Error())
@ -617,17 +627,25 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin
}
flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC
if opts.IfNoneMatch {
// TODO handle IfNoneMatch == ETag
if opts.IfNoneMatch.IsWildcard() {
// Make sure we're not overwriting an existing file
flags |= os.O_EXCL
} else if opts.IfMatch == "*" {
} else if opts.IfMatch.IsWildcard() {
// Make sure we _are_ overwriting an existing file
flags &= ^os.O_CREATE
} else if opts.IfMatch != "" {
} else if opts.IfMatch.IsSet() {
// Make sure we overwrite the _right_ file
etag, err := etagForFile(localPath)
if err != nil {
return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
}
if opts.IfMatch != etag {
err = fmt.Errorf("If-Match does not match current ETag (%s/%s)", opts.IfMatch, etag)
want, err := opts.IfMatch.ETag()
if err != nil {
return "", webdav.NewHTTPError(http.StatusBadRequest, err)
}
if want != etag {
err = fmt.Errorf("If-Match does not match current ETag (%s/%s)", want, etag)
return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
}
}