diff --git a/storage/filesystem_caldav.go b/storage/filesystem_caldav.go index 3b97c25..1162fd0 100644 --- a/storage/filesystem_caldav.go +++ b/storage/filesystem_caldav.go @@ -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 .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 } - return objPath, nil + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } + info, err := f.Stat() + if err != nil { + return nil, err + } + + 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 { diff --git a/storage/filesystem_carddav.go b/storage/filesystem_carddav.go index a2d82a9..764dc10 100644 --- a/storage/filesystem_carddav.go +++ b/storage/filesystem_carddav.go @@ -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 } - homeSetPath, err_ := b.AddressBookHomeSetPath(ctx) - if err_ != nil { - return nil, fmt.Errorf("error creating default address book: %s", err_.Error()) + 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 +} + +func (b *filesystemBackend) createAddressBook(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.createAddressBook()") + + 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) + "/" - log.Debug().Str("local", localPath).Str("url", urlPath).Msg("filesystem.createDefaultAddressBook()") - + // 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 .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 != nil { - return "", err + if err := enc.Encode(card); err != nil { + return nil, err } - return objPath, nil + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } + info, err := f.Stat() + if err != nil { + return nil, err + } + + 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 { diff --git a/storage/postgresql.go b/storage/postgresql.go index 25c0972..0100ab4 100644 --- a/storage/postgresql.go +++ b/storage/postgresql.go @@ -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") }