~sircmpwn/tokidoki

adb2a8bdfb74b7687e53cf70131025e391a9e679 — Conrad Hoffmann 3 months ago 39f9068
storage: adapt to go-webdav interface changes
3 files changed, 138 insertions(+), 39 deletions(-)

M storage/filesystem_caldav.go
M storage/filesystem_carddav.go
M storage/postgresql.go
M storage/filesystem_caldav.go => storage/filesystem_caldav.go +30 -10
@@ 225,6 225,10 @@ func (b *filesystemBackend) GetCalendar(ctx context.Context, urlPath string) (*c
	return &calendar, nil
}

func (b *filesystemBackend) CreateCalendar(ctx context.Context, calendar *caldav.Calendar) error {
	panic("TODO")
}

func (b *filesystemBackend) GetCalendarObject(ctx context.Context, objPath string, req *caldav.CalendarCompRequest) (*caldav.CalendarObject, error) {
	log.Debug().Str("path", objPath).Msg("filesystem.GetCalendarObject()")



@@ 299,12 303,12 @@ func (b *filesystemBackend) QueryCalendarObjects(ctx context.Context, urlPath st
	return filtered, err
}

func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath string, calendar *ical.Calendar, opts *caldav.PutCalendarObjectOptions) (loc string, err error) {
func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath string, calendar *ical.Calendar, opts *caldav.PutCalendarObjectOptions) (*caldav.CalendarObject, error) {
	log.Debug().Str("path", objPath).Msg("filesystem.PutCalendarObject()")

	_, uid, err := caldav.ValidateCalendarObject(calendar)
	if err != nil {
		return "", caldav.NewPreconditionError(caldav.PreconditionValidCalendarObjectResource)
		return nil, caldav.NewPreconditionError(caldav.PreconditionValidCalendarObjectResource)
	}

	// Object always get saved as <UID>.ics


@@ 313,7 317,7 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin

	localPath, err := b.safeLocalCalDAVPath(ctx, objPath)
	if err != nil {
		return "", err
		return nil, err
	}

	flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC


@@ 328,33 332,49 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin
		// Make sure we overwrite the _right_ file
		etag, err := etagForFile(localPath)
		if err != nil {
			return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
			return nil, webdav.NewHTTPError(http.StatusPreconditionFailed, err)
		}
		want, err := opts.IfMatch.ETag()
		if err != nil {
			return "", webdav.NewHTTPError(http.StatusBadRequest, err)
			return nil, 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)
			return nil, webdav.NewHTTPError(http.StatusPreconditionFailed, err)
		}
	}

	f, err := os.OpenFile(localPath, flags, 0666)
	if os.IsExist(err) {
		return "", caldav.NewPreconditionError(caldav.PreconditionNoUIDConflict)
		return nil, caldav.NewPreconditionError(caldav.PreconditionNoUIDConflict)
	} else if err != nil {
		return "", err
		return nil, err
	}
	defer f.Close()

	enc := ical.NewEncoder(f)
	err = enc.Encode(calendar)
	if err != nil {
		return "", err
		return nil, err
	}

	etag, err := etagForFile(localPath)
	if err != nil {
		return nil, err
	}
	info, err := f.Stat()
	if err != nil {
		return nil, err
	}

	return objPath, nil
	r := caldav.CalendarObject{
		Path:          objPath,
		ModTime:       info.ModTime(),
		ContentLength: info.Size(),
		ETag:          etag,
		Data:          calendar,
	}
	return &r, nil
}

func (b *filesystemBackend) DeleteCalendarObject(ctx context.Context, path string) error {

M storage/filesystem_carddav.go => storage/filesystem_carddav.go +95 -28
@@ 129,22 129,51 @@ func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath s
	return result, err
}

func (b *filesystemBackend) createDefaultAddressBook(ctx context.Context) (*carddav.AddressBook, error) {
	// TODO what should the default address book look like?
	localPath, err_ := b.localCardDAVDir(ctx, defaultResourceName)
	if err_ != nil {
		return nil, fmt.Errorf("error creating default address book: %s", err_.Error())
func (b *filesystemBackend) writeAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	localPath, err := b.safeLocalCardDAVPath(ctx, ab.Path)
	if err != nil {
		return err
	}

	log.Debug().Str("local", localPath).Str("url", ab.Path).Msg("filesystem.writeAddressBook()")

	blob, err := json.MarshalIndent(ab, "", "  ")
	if err != nil {
		return err
	}
	return os.WriteFile(path.Join(localPath, addressBookFileName), blob, 0644)
	if err != nil {
		return fmt.Errorf("error writing address book: %s", err.Error())
	}
	return nil
}

	homeSetPath, err_ := b.AddressBookHomeSetPath(ctx)
	if err_ != nil {
		return nil, fmt.Errorf("error creating default address book: %s", err_.Error())
func (b *filesystemBackend) createAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	localPath, err := b.safeLocalCardDAVPath(ctx, ab.Path)
	if err != nil {
		return err
	}

	urlPath := path.Join(homeSetPath, defaultResourceName) + "/"
	log.Debug().Str("local", localPath).Str("url", ab.Path).Msg("filesystem.createAddressBook()")

	log.Debug().Str("local", localPath).Str("url", urlPath).Msg("filesystem.createDefaultAddressBook()")
	err = os.Mkdir(localPath, 0755)
	if err != nil {
		return fmt.Errorf("error creating address book: %s", err.Error())
	}
	return b.writeAddressBook(ctx, ab)
}

func (b *filesystemBackend) createDefaultAddressBook(ctx context.Context) (*carddav.AddressBook, error) {
	log.Debug().Msg("filesystem.createDefaultAddressBook()")

	homeSetPath, err := b.AddressBookHomeSetPath(ctx)
	if err != nil {
		return nil, fmt.Errorf("error creating default address book: %s", err.Error())
	}

	urlPath := path.Join(homeSetPath, defaultResourceName) + "/"

	// TODO what should the default address book look like?
	defaultAB := carddav.AddressBook{
		Path:                 urlPath,
		Name:                 "My contacts",


@@ 152,14 181,8 @@ func (b *filesystemBackend) createDefaultAddressBook(ctx context.Context) (*card
		MaxResourceSize:      1024,
		SupportedAddressData: nil,
	}
	blob, err := json.MarshalIndent(defaultAB, "", "  ")
	if err != nil {
		return nil, fmt.Errorf("error creating default address book: %s", err.Error())
	}
	err = os.WriteFile(path.Join(localPath, addressBookFileName), blob, 0644)
	if err != nil {
		return nil, fmt.Errorf("error writing default address book: %s", err.Error())
	}
	err = b.createAddressBook(ctx, &defaultAB)
	log.Debug().Err(err).Msg("filesystem.createDefaultAddressBook() done")
	return &defaultAB, nil
}



@@ 243,6 266,35 @@ func (b *filesystemBackend) GetAddressBook(ctx context.Context, urlPath string) 
	return &addressBook, nil
}

func (b *filesystemBackend) CreateAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	log.Debug().Str("path", ab.Path).Msg("filesystem.CreateAddressBook()")
	ab.MaxResourceSize = 4096
	err := b.createAddressBook(ctx, ab)
	log.Debug().Err(err).Msg("filesystem.CreateAddressBook() done")
	return err
}

func (b *filesystemBackend) UpdateAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	log.Debug().Str("path", ab.Path).Msg("filesystem.UpdateAddressBook()")
	ab.MaxResourceSize = 4096
	err := b.writeAddressBook(ctx, ab)
	log.Debug().Err(err).Msg("filesystem.UpdateAddressBook() done")
	return err
}

func (b *filesystemBackend) DeleteAddressBook(ctx context.Context, urlPath string) error {
	log.Debug().Str("path", urlPath).Msg("filesystem.DeleteAddressBook()")

	localPath, err := b.safeLocalCardDAVPath(ctx, urlPath)
	if err != nil {
		return err
	}
	log.Debug().Str("path", localPath).Msg("deleting addressbook")
	err = os.RemoveAll(localPath)
	log.Debug().Err(err).Msg("filesystem.DeleteAddressBook() done")
	return err
}

func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string, req *carddav.AddressDataRequest) (*carddav.AddressObject, error) {
	log.Debug().Str("path", objPath).Msg("filesystem.GetAddressObject()")



@@ 317,7 369,7 @@ func (b *filesystemBackend) QueryAddressObjects(ctx context.Context, urlPath str
	return filtered, err
}

func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string, card vcard.Card, opts *carddav.PutAddressObjectOptions) (loc string, err error) {
func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string, card vcard.Card, opts *carddav.PutAddressObjectOptions) (*carddav.AddressObject, error) {
	log.Debug().Str("path", objPath).Msg("filesystem.PutAddressObject()")

	// Object always get saved as <UID>.vcf


@@ 326,7 378,7 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string

	localPath, err := b.safeLocalCardDAVPath(ctx, objPath)
	if err != nil {
		return "", err
		return nil, err
	}

	flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC


@@ 341,33 393,48 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string
		// Make sure we overwrite the _right_ file
		etag, err := etagForFile(localPath)
		if err != nil {
			return "", webdav.NewHTTPError(http.StatusPreconditionFailed, err)
			return nil, webdav.NewHTTPError(http.StatusPreconditionFailed, err)
		}
		want, err := opts.IfMatch.ETag()
		if err != nil {
			return "", webdav.NewHTTPError(http.StatusBadRequest, err)
			return nil, 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)
			return nil, webdav.NewHTTPError(http.StatusPreconditionFailed, err)
		}
	}

	f, err := os.OpenFile(localPath, flags, 0666)
	if os.IsExist(err) {
		return "", carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict)
		return nil, carddav.NewPreconditionError(carddav.PreconditionNoUIDConflict)
	} else if err != nil {
		return "", err
		return nil, err
	}
	defer f.Close()

	enc := vcard.NewEncoder(f)
	err = enc.Encode(card)
	if err := enc.Encode(card); err != nil {
		return nil, err
	}

	etag, err := etagForFile(localPath)
	if err != nil {
		return "", err
		return nil, err
	}
	info, err := f.Stat()
	if err != nil {
		return nil, err
	}

	return objPath, nil
	r := carddav.AddressObject{
		Path:          objPath,
		ModTime:       info.ModTime(),
		ContentLength: info.Size(),
		ETag:          etag,
		Card:          card,
	}
	return &r, nil
}

func (b *filesystemBackend) DeleteAddressObject(ctx context.Context, path string) error {

M storage/postgresql.go => storage/postgresql.go +13 -1
@@ 32,6 32,18 @@ func (*psqlBackend) GetAddressBook(ctx context.Context, path string) (*carddav.A
	panic("TODO")
}

func (*psqlBackend) CreateAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	panic("TODO")
}

func (*psqlBackend) UpdateAddressBook(ctx context.Context, ab *carddav.AddressBook) error {
	panic("TODO")
}

func (*psqlBackend) DeleteAddressBook(ctx context.Context, path string) error {
	panic("TODO")
}

func (*psqlBackend) GetAddressObject(ctx context.Context, path string, req *carddav.AddressDataRequest) (*carddav.AddressObject, error) {
	panic("TODO")
}


@@ 44,7 56,7 @@ func (*psqlBackend) QueryAddressObjects(ctx context.Context, path string, query 
	panic("TODO")
}

func (*psqlBackend) PutAddressObject(ctx context.Context, path string, card vcard.Card, opts *carddav.PutAddressObjectOptions) (loc string, err error) {
func (*psqlBackend) PutAddressObject(ctx context.Context, path string, card vcard.Card, opts *carddav.PutAddressObjectOptions) (*carddav.AddressObject, error) {
	panic("TODO")
}