storage/filesystem: implement more operations

Everything except QueryAddressObjects is now functional, though not
feature-complete. Simple operations work, e.g. via Evolution.
This commit is contained in:
Conrad Hoffmann 2022-02-24 12:54:30 +01:00
parent 65346f0317
commit 8b46585109

View File

@ -2,6 +2,8 @@ package storage
import (
"context"
"crypto/md5"
"encoding/base64"
"encoding/json"
"fmt"
"io/ioutil"
@ -56,6 +58,31 @@ func (b *filesystemBackend) pathForContext(ctx context.Context) (string, error)
return path, nil
}
func etagForFile(path string) (string, error) {
data, err := ioutil.ReadFile(path)
if err != nil {
return "", err
}
csum := md5.Sum(data)
return base64.StdEncoding.EncodeToString(csum[:]), nil
}
func vcardFromFile(path string) (*vcard.Card, error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}
defer f.Close()
dec := vcard.NewDecoder(f)
card, err := dec.Decode()
if err != nil {
return nil, err
}
return &card, nil
}
func createDefaultAddressBook(path string) error {
// TODO what should the default address book look like?
defaultAB := carddav.AddressBook{
@ -105,30 +132,114 @@ func (b *filesystemBackend) AddressBook(ctx context.Context) (*carddav.AddressBo
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 (b *filesystemBackend) GetAddressObject(ctx context.Context, path string, req *carddav.AddressDataRequest) (*carddav.AddressObject, error) {
basePath, err := b.pathForContext(ctx)
if err != nil {
return nil, err
}
path = filepath.Join(basePath, path)
info, err := os.Stat(path)
if err != nil {
return nil, err
}
card, err := vcardFromFile(path)
if err != nil {
return nil, err
}
etag, err := etagForFile(path)
if err != nil {
return nil, err
}
obj := carddav.AddressObject{
Path: "/" + filepath.Base(path),
ModTime: info.ModTime(),
ETag: etag,
Card: *card,
}
return &obj, nil
}
func (*filesystemBackend) ListAddressObjects(ctx context.Context, req *carddav.AddressDataRequest) ([]carddav.AddressObject, error) {
return []carddav.AddressObject{}, nil
//panic("TODO")
func (b *filesystemBackend) ListAddressObjects(ctx context.Context, req *carddav.AddressDataRequest) ([]carddav.AddressObject, error) {
result := []carddav.AddressObject{}
path, err := b.pathForContext(ctx)
if err != nil {
return result, err
}
err = filepath.Walk(path, func(filename string, info os.FileInfo, err error) error {
// TODO this heuristic will not work for all clients
if filepath.Ext(filename) != ".vcf" {
return nil
}
card, err := vcardFromFile(filename)
if err != nil {
return err
}
etag, err := etagForFile(filename)
if err != nil {
return err
}
obj := carddav.AddressObject{
Path: "/" + filepath.Base(filename),
ModTime: info.ModTime(),
ETag: etag,
Card: *card,
}
result = append(result, obj)
return nil
})
if err != nil {
panic(err)
}
return result, nil
}
func (*filesystemBackend) QueryAddressObjects(ctx context.Context, query *carddav.AddressBookQuery) ([]carddav.AddressObject, error) {
// TODO
log.Println("QueryAddressObjects called, not implemented")
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 (b *filesystemBackend) PutAddressObject(ctx context.Context, path string, card vcard.Card) (loc string, err error) {
basePath, err := b.pathForContext(ctx)
if err != nil {
return "", err
}
path = filepath.Join(basePath, path)
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0644)
if err != nil {
return "", err
}
defer f.Close()
enc := vcard.NewEncoder(f)
err = enc.Encode(card)
if err != nil {
return "", err
}
return path, nil
}
func (*filesystemBackend) DeleteAddressObject(ctx context.Context, path string) error {
//TODO
log.Fatalf("DeleteAddressObject(ctx, '%s'): not implemented", path)
func (b *filesystemBackend) DeleteAddressObject(ctx context.Context, path string) error {
basePath, err := b.pathForContext(ctx)
if err != nil {
return err
}
path = filepath.Join(basePath, path)
//TODO does this need more security/sanity checks?
err = os.Remove(path)
if err != nil {
return err
}
return nil
}