tokidoki/storage/filesystem.go
2022-02-23 21:09:20 +01:00

135 lines
3.6 KiB
Go

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 nilBackend carddav.Backend = (*filesystemBackend)(nil)
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 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 (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) 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) ListAddressObjects(ctx context.Context, req *carddav.AddressDataRequest) ([]carddav.AddressObject, error) {
return []carddav.AddressObject{}, nil
//panic("TODO")
}
func (*filesystemBackend) QueryAddressObjects(ctx context.Context, query *carddav.AddressBookQuery) ([]carddav.AddressObject, error) {
return []carddav.AddressObject{}, nil
//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
}