From d917938a293c389289aaa758370de1134bcdd92a Mon Sep 17 00:00:00 2001 From: Conrad Hoffmann Date: Tue, 22 Feb 2022 18:24:17 +0100 Subject: [PATCH] Start simple filesystem storage backend --- cmd/tokidoki/main.go | 6 ++- storage/filesystem.go | 120 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 111 insertions(+), 15 deletions(-) diff --git a/cmd/tokidoki/main.go b/cmd/tokidoki/main.go index 23018b9..94f64ec 100644 --- a/cmd/tokidoki/main.go +++ b/cmd/tokidoki/main.go @@ -41,7 +41,11 @@ func main() { } mux.Use(authProvider.Middleware()) - backend := storage.NewPostgreSQL() + //backend := storage.NewPostgreSQL() + backend, err := storage.NewFilesystem("./teststorage") + if err != nil { + log.Fatalf("failed to load storage backend: %s", err.Error()) + } mux.Mount("/", &carddav.Handler{Backend: backend}) server := http.Server{ diff --git a/storage/filesystem.go b/storage/filesystem.go index 431157e..9f54b81 100644 --- a/storage/filesystem.go +++ b/storage/filesystem.go @@ -1,42 +1,134 @@ package storage import ( + "context" + "encoding/json" + "fmt" + "io/ioutil" + "log" + "os" + "path/filepath" + "github.com/emersion/go-vcard" "github.com/emersion/go-webdav/carddav" + + "git.sr.ht/~sircmpwn/tokidoki/auth" ) type filesystemBackend struct { path string } -var _ carddav.Backend = (*filesystemBackend)(nil) +var nilBackend carddav.Backend = (*filesystemBackend)(nil) -func NewfilesystemBackend(path string) carddav.Backend { +func NewFilesystem(path string) (carddav.Backend, error) { + info, err := os.Stat(path) + if err != nil { + return nilBackend, fmt.Errorf("failed to create filesystem backend: %s", err.Error()) + } + if !info.IsDir() { + return nilBackend, fmt.Errorf("base path for filesystem backend must be a directory") + } return &filesystemBackend{ path: path, + }, nil +} + +func (b *filesystemBackend) pathForContext(ctx context.Context) (string, error) { + raw := ctx.Value(auth.AuthCtxKey) + if raw == nil { + return b.path, nil } + authCtx, ok := raw.(*auth.AuthContext) + if !ok { + panic("Invalid data in auth context!") + } + //TODO sanitize user name or at least check if valid dir name? + path := filepath.Join(b.path, authCtx.UserName) + + _, err := os.Stat(path) + if os.IsNotExist(err) { + err = os.Mkdir(path, 0755) + if err != nil { + return "", fmt.Errorf("error creating '%s': %s", path, err.Error()) + } + } + return path, nil } -func (*filesystemBackend) AddressBook() (*carddav.AddressBook, error) { - panic("TODO") +func createDefaultAddressBook(path string) error { + // TODO what should the default address book look like? + defaultAB := carddav.AddressBook{ + Path: "/default", + Name: "My contacts", + Description: "Default address book", + MaxResourceSize: 1024, + SupportedAddressData: []carddav.AddressDataType{}, + } + blob, err := json.MarshalIndent(defaultAB, "", " ") + if err != nil { + return fmt.Errorf("error creating default address book: %s", err.Error()) + } + err = os.WriteFile(path, blob, 0644) + if err != nil { + return fmt.Errorf("error writing default address book: %s", err.Error()) + } + return nil } -func (*filesystemBackend) GetAddressObject(path string, req *carddav.AddressDataRequest) (*carddav.AddressObject, error) { - panic("TODO") +func (b *filesystemBackend) AddressBook(ctx context.Context) (*carddav.AddressBook, error) { + path, err := b.pathForContext(ctx) + if err != nil { + return nil, err + } + path = filepath.Join(path, "default.json") + _, err = os.Stat(path) + if os.IsNotExist(err) { + err = createDefaultAddressBook(path) + if err != nil { + return nil, err + } + } else if err != nil { + return nil, fmt.Errorf("error opening address book: %s", err.Error()) + } + + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error opening address book: %s", err.Error()) + } + var addressBook carddav.AddressBook + err = json.Unmarshal(data, &addressBook) + if err != nil { + return nil, fmt.Errorf("error reading address book: %s", err.Error()) + } + + return &addressBook, nil } -func (*filesystemBackend) ListAddressObjects(req *carddav.AddressDataRequest) ([]carddav.AddressObject, error) { - panic("TODO") +func (*filesystemBackend) GetAddressObject(ctx context.Context, path string, req *carddav.AddressDataRequest) (*carddav.AddressObject, error) { + //TODO + log.Fatalf("PutAddressObject(ctx, '%s', req): not implemented", path) + return nil, nil } -func (*filesystemBackend) QueryAddressObjects(query *carddav.AddressBookQuery) ([]carddav.AddressObject, error) { - panic("TODO") +func (*filesystemBackend) ListAddressObjects(ctx context.Context, req *carddav.AddressDataRequest) ([]carddav.AddressObject, error) { + return []carddav.AddressObject{}, nil + //panic("TODO") } -func (*filesystemBackend) PutAddressObject(path string, card vcard.Card) (loc string, err error) { - panic("TODO") +func (*filesystemBackend) QueryAddressObjects(ctx context.Context, query *carddav.AddressBookQuery) ([]carddav.AddressObject, error) { + return []carddav.AddressObject{}, nil + //panic("TODO") } -func (*filesystemBackend) DeleteAddressObject(path string) error { - panic("TODO") +func (*filesystemBackend) PutAddressObject(ctx context.Context, path string, card vcard.Card) (loc string, err error) { + //TODO + log.Fatalf("PutAddressObject(ctx, '%s', card): not implemented", path) + return "", nil +} + +func (*filesystemBackend) DeleteAddressObject(ctx context.Context, path string) error { + //TODO + log.Fatalf("DeleteAddressObject(ctx, '%s'): not implemented", path) + return nil }