diff --git a/Makefile b/Makefile deleted file mode 100644 index 941edb3..0000000 --- a/Makefile +++ /dev/null @@ -1,12 +0,0 @@ - -build: - go build ./cmd/tokidoki - -debug: - go build -tags "pam nullauth" ./cmd/tokidoki - -fmt: - go fmt ./... - -lint: - golangci-lint run diff --git a/cmd/tokidoki/main.go b/cmd/tokidoki/main.go index 87fd1b5..9d706fd 100644 --- a/cmd/tokidoki/main.go +++ b/cmd/tokidoki/main.go @@ -207,10 +207,7 @@ func main() { for sig := range sigCh { switch sig { case syscall.SIGINT, syscall.SIGTERM: - err := server.Shutdown(context.Background()) - if err != nil { - log.Fatal().Err(err).Msg("Shutdown() error") - } + server.Shutdown(context.Background()) return } } diff --git a/go.mod b/go.mod index 08c3eb5..0c7d558 100644 --- a/go.mod +++ b/go.mod @@ -3,22 +3,22 @@ module git.sr.ht/~sircmpwn/tokidoki go 1.18 require ( - git.sr.ht/~emersion/go-oauth2 v0.0.0-20240226120011-78f10ffd1d51 + git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088 github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6 - github.com/emersion/go-imap/v2 v2.0.0-beta.4 - github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff - github.com/emersion/go-webdav v0.5.1-0.20240713135526-7f8c17ad7135 - github.com/go-chi/chi/v5 v5.1.0 + github.com/emersion/go-imap/v2 v2.0.0-beta.2.0.20240417100641-a587a14d3f01 + github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9 + github.com/emersion/go-webdav v0.5.1-0.20240419143909-21f251fa1de2 + github.com/go-chi/chi/v5 v5.0.12 github.com/msteinert/pam/v2 v2.0.0 - github.com/rs/zerolog v1.33.0 - golang.org/x/crypto v0.28.0 + github.com/rs/zerolog v1.32.0 + golang.org/x/crypto v0.18.0 ) require ( github.com/emersion/go-message v0.18.1 // indirect - github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 // indirect + github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/teambition/rrule-go v1.8.2 // indirect - golang.org/x/sys v0.26.0 // indirect + golang.org/x/sys v0.19.0 // indirect ) diff --git a/go.sum b/go.sum index d9d31a4..f2a698e 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,20 @@ -git.sr.ht/~emersion/go-oauth2 v0.0.0-20240226120011-78f10ffd1d51 h1:iz8Tm7obSouGC0atCd+NtFSmCgfxDizXD1Rm+0Jw75w= -git.sr.ht/~emersion/go-oauth2 v0.0.0-20240226120011-78f10ffd1d51/go.mod h1:VHj0jSCLIkrfEwmOvJ4+ykpoVbD/YLN7BM523oKKBHc= +git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088 h1:KuPliLD8CQM1WbCHdjHR6mhadIzLaAJCNENmvB1y9gs= +git.sr.ht/~emersion/go-oauth2 v0.0.0-20240217160856-2e0d6e20b088/go.mod h1:VHj0jSCLIkrfEwmOvJ4+ykpoVbD/YLN7BM523oKKBHc= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6 h1:kHoSgklT8weIDl6R6xFpBJ5IioRdBU1v2X2aCZRVCcM= github.com/emersion/go-ical v0.0.0-20240127095438-fc1c9d8fb2b6/go.mod h1:BEksegNspIkjCQfmzWgsgbu6KdeJ/4LwUZs7DMBzjzw= -github.com/emersion/go-imap/v2 v2.0.0-beta.4 h1:BS7+kUVhe/jfuFWgn8li0AbCKBIDoNvqJWsRJppltcc= -github.com/emersion/go-imap/v2 v2.0.0-beta.4/go.mod h1:BZTFHsS1hmgBkFlHqbxGLXk2hnRqTItUgwjSSCsYNAk= +github.com/emersion/go-imap/v2 v2.0.0-beta.2.0.20240417100641-a587a14d3f01 h1:dq/06hDbCT+/DpbKWSrfrTeiJW97ION78N6J6Mktp2w= +github.com/emersion/go-imap/v2 v2.0.0-beta.2.0.20240417100641-a587a14d3f01/go.mod h1:BZTFHsS1hmgBkFlHqbxGLXk2hnRqTItUgwjSSCsYNAk= github.com/emersion/go-message v0.18.1 h1:tfTxIoXFSFRwWaZsgnqS1DSZuGpYGzSmCZD8SK3QA2E= github.com/emersion/go-message v0.18.1/go.mod h1:XpJyL70LwRvq2a8rVbHXikPgKj8+aI0kGdHlg16ibYA= -github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk= -github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= +github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43 h1:hH4PQfOndHDlpzYfLAAfl63E8Le6F2+EL/cdhlkyRJY= +github.com/emersion/go-sasl v0.0.0-20231106173351-e73c9f7bad43/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ= +github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9 h1:ATgqloALX6cHCranzkLb8/zjivwQ9DWWDCQRnxTPfaA= github.com/emersion/go-vcard v0.0.0-20230815062825-8fda7d206ec9/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM= -github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff h1:4N8wnS3f1hNHSmFD5zgFkWCyA4L1kCDkImPAtK7D6tg= -github.com/emersion/go-vcard v0.0.0-20241024213814-c9703dde27ff/go.mod h1:HMJKR5wlh/ziNp+sHEDV2ltblO4JD2+IdDOWtGcQBTM= -github.com/emersion/go-webdav v0.5.1-0.20240713135526-7f8c17ad7135 h1:Ssk00uh7jhctJ23eclGxhhGqplSQB+wCt6fmbjhnOS8= -github.com/emersion/go-webdav v0.5.1-0.20240713135526-7f8c17ad7135/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ= -github.com/go-chi/chi/v5 v5.1.0 h1:acVI1TYaD+hhedDJ3r54HyA6sExp3HfXq7QWEEY/xMw= -github.com/go-chi/chi/v5 v5.1.0/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= +github.com/emersion/go-webdav v0.5.1-0.20240419143909-21f251fa1de2 h1:k/NO/RfeXFuKGcpHDkspYoE8u6tWoHs03tH5DXg22To= +github.com/emersion/go-webdav v0.5.1-0.20240419143909-21f251fa1de2/go.mod h1:mI8iBx3RAODwX7PJJ7qzsKAKs/vY429YfS2/9wKnDbQ= +github.com/go-chi/chi/v5 v5.0.12 h1:9euLV5sTrTNTRUU9POmDUvfxyj6LAABLUcEWO+JJb4s= +github.com/go-chi/chi/v5 v5.0.12/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= @@ -27,15 +26,15 @@ github.com/msteinert/pam/v2 v2.0.0 h1:jnObb8MT6jvMbmrUQO5J/puTUjxy7Av+55zVJRJsCy github.com/msteinert/pam/v2 v2.0.0/go.mod h1:KT28NNIcDFf3PcBmNI2mIGO4zZJ+9RSs/At2PB3IDVc= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= -github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8= -github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= +github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0= +github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss= github.com/teambition/rrule-go v1.8.2 h1:lIjpjvWTj9fFUZCmuoVDrKVOtdiyzbzc93qTmRVe/J8= github.com/teambition/rrule-go v1.8.2/go.mod h1:Ieq5AbrKGciP1V//Wq8ktsTXwSwJHDD5mD/wLBGl3p4= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw= -golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U= +golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -54,12 +53,12 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= -golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= +golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= diff --git a/storage/etag.go b/storage/etag.go deleted file mode 100644 index 1d22a5e..0000000 --- a/storage/etag.go +++ /dev/null @@ -1,58 +0,0 @@ -package storage - -import ( - "crypto/sha1" - "encoding/base64" - "hash" - "io" -) - -type ETagWriter struct { - io.Writer - output io.Writer - csum hash.Hash -} - -// NewETagWriter creates a new io.Writer that pipes writes to the provided -// output while also calculating the contents ETag. -func NewETagWriter(output io.Writer) *ETagWriter { - csum := sha1.New() - return &ETagWriter{ - output: io.MultiWriter(output, csum), - csum: csum, - } -} - -func (e *ETagWriter) Write(p []byte) (n int, err error) { - return e.output.Write(p) -} - -func (e *ETagWriter) ETag() string { - csum := e.csum.Sum(nil) - return base64.StdEncoding.EncodeToString(csum[:]) -} - -type ETagReader struct { - io.Reader - input io.Reader - csum hash.Hash -} - -// NewETagReader creates a new io.Reader that pipes reads from the provided -// input while also calculating the contents ETag. -func NewETagReader(input io.Reader) *ETagReader { - csum := sha1.New() - return &ETagReader{ - input: io.TeeReader(input, csum), - csum: csum, - } -} - -func (e *ETagReader) Read(p []byte) (n int, err error) { - return e.input.Read(p) -} - -func (e *ETagReader) ETag() string { - csum := e.csum.Sum(nil) - return base64.StdEncoding.EncodeToString(csum[:]) -} diff --git a/storage/filesystem.go b/storage/filesystem.go index 301c8f7..afc058b 100644 --- a/storage/filesystem.go +++ b/storage/filesystem.go @@ -1,6 +1,8 @@ package storage import ( + "crypto/sha1" + "encoding/base64" "fmt" "io" "os" @@ -8,7 +10,6 @@ import ( "path/filepath" "regexp" "strings" - "sync" "github.com/rs/zerolog/log" @@ -19,13 +20,9 @@ import ( type filesystemBackend struct { webdav.UserPrincipalBackend - path string caldavPrefix string carddavPrefix string - - // maps file path to *sync.RWMutex - locks sync.Map } var ( @@ -50,36 +47,6 @@ func NewFilesystem(fsPath, caldavPrefix, carddavPrefix string, userPrincipalBack return backend, backend, nil } -func (b *filesystemBackend) RLock(filename string) { - lock_, _ := b.locks.LoadOrStore(filename, &sync.RWMutex{}) - lock := lock_.(*sync.RWMutex) - lock.RLock() -} - -func (b *filesystemBackend) RUnlock(filename string) { - lock_, ok := b.locks.Load(filename) - if !ok { - panic("attempt to unlock non-existing lock") - } - lock := lock_.(*sync.RWMutex) - lock.RUnlock() -} - -func (b *filesystemBackend) Lock(filename string) { - lock_, _ := b.locks.LoadOrStore(filename, &sync.RWMutex{}) - lock := lock_.(*sync.RWMutex) - lock.Lock() -} - -func (b *filesystemBackend) Unlock(filename string) { - lock_, ok := b.locks.Load(filename) - if !ok { - panic("attempt to unlock non-existing lock") - } - lock := lock_.(*sync.RWMutex) - lock.Unlock() -} - func ensureLocalDir(path string) error { if _, err := os.Stat(path); os.IsNotExist(err) { err = os.MkdirAll(path, 0755) @@ -142,10 +109,11 @@ func etagForFile(path string) (string, error) { } defer f.Close() - src := NewETagReader(f) - if _, err := io.ReadAll(src); err != nil { + h := sha1.New() + if _, err := io.Copy(h, f); err != nil { return "", err } + csum := h.Sum(nil) - return src.ETag(), nil + return base64.StdEncoding.EncodeToString(csum[:]), nil } diff --git a/storage/filesystem_caldav.go b/storage/filesystem_caldav.go index fdab921..1162fd0 100644 --- a/storage/filesystem_caldav.go +++ b/storage/filesystem_caldav.go @@ -47,23 +47,22 @@ func (b *filesystemBackend) safeLocalCalDAVPath(ctx context.Context, urlPath str return b.safeLocalPath(homeSetPath, urlPath) } -func calendarAndEtagFromFile(path string, propFilter []string) (*ical.Calendar, string, error) { +func calendarFromFile(path string, propFilter []string) (*ical.Calendar, error) { f, err := os.Open(path) if err != nil { - return nil, "", err + return nil, err } defer f.Close() - src := NewETagReader(f) - dec := ical.NewDecoder(src) + dec := ical.NewDecoder(f) cal, err := dec.Decode() if err != nil { - return nil, "", err + return nil, err } + return cal, nil // TODO implement //return icalPropFilter(cal, propFilter), nil - return cal, src.ETag(), nil } func (b *filesystemBackend) loadAllCalendarObjects(ctx context.Context, urlPath string, propFilter []string) ([]caldav.CalendarObject, error) { @@ -86,15 +85,17 @@ func (b *filesystemBackend) loadAllCalendarObjects(ctx context.Context, urlPath return nil } - b.RLock(filename) - defer b.RUnlock(filename) - - cal, etag, err := calendarAndEtagFromFile(filename, propFilter) + cal, err := calendarFromFile(filename, propFilter) if err != nil { fmt.Printf("load calendar error for %s: %v\n", filename, err) return err } + etag, err := etagForFile(filename) + if err != nil { + return err + } + // TODO can this potentially be called on a calendar object resource? // Would work (as Walk() includes root), except for the path construction below obj := caldav.CalendarObject{ @@ -104,7 +105,6 @@ func (b *filesystemBackend) loadAllCalendarObjects(ctx context.Context, urlPath ETag: etag, Data: cal, } - log.Debug().Str("path", obj.Path).Str("etag", etag).Int64("size", info.Size()).Msg("calendar object loaded") result = append(result, obj) return nil }) @@ -138,12 +138,7 @@ func (b *filesystemBackend) createDefaultCalendar(ctx context.Context) (*caldav. if err != nil { return nil, fmt.Errorf("error creating default calendar: %s", err.Error()) } - - filename := path.Join(localPath, calendarFileName) - b.Lock(filename) - defer b.Unlock(filename) - - err = os.WriteFile(filename, blob, 0644) + err = os.WriteFile(path.Join(localPath, calendarFileName), blob, 0644) if err != nil { return nil, fmt.Errorf("error writing default calendar: %s", err.Error()) } @@ -172,10 +167,6 @@ func (b *filesystemBackend) ListCalendars(ctx context.Context) ([]caldav.Calenda } calPath := path.Join(filename, calendarFileName) - - b.RLock(calPath) - defer b.RUnlock(calPath) - data, err := os.ReadFile(calPath) if err != nil { if os.IsNotExist(err) { @@ -218,9 +209,6 @@ func (b *filesystemBackend) GetCalendar(ctx context.Context, urlPath string) (*c log.Debug().Str("path", localPath).Msg("loading calendar") - b.RLock(localPath) - defer b.RUnlock(localPath) - data, err := os.ReadFile(localPath) if err != nil { if os.IsNotExist(err) { @@ -249,9 +237,6 @@ func (b *filesystemBackend) GetCalendarObject(ctx context.Context, objPath strin return nil, err } - b.RLock(localPath) - defer b.RUnlock(localPath) - info, err := os.Stat(localPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -265,12 +250,17 @@ func (b *filesystemBackend) GetCalendarObject(ctx context.Context, objPath strin propFilter = req.Props } - calendar, etag, err := calendarAndEtagFromFile(localPath, propFilter) + calendar, err := calendarFromFile(localPath, propFilter) if err != nil { log.Debug().Str("path", localPath).Err(err).Msg("error reading calendar") return nil, err } + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } + obj := caldav.CalendarObject{ Path: objPath, ModTime: info.ModTime(), @@ -278,7 +268,6 @@ func (b *filesystemBackend) GetCalendarObject(ctx context.Context, objPath strin ETag: etag, Data: calendar, } - log.Debug().Str("path", objPath).Str("etag", etag).Int64("size", info.Size()).Msg("returning calendar object") return &obj, nil } @@ -331,9 +320,6 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin return nil, err } - b.Lock(localPath) - defer b.Unlock(localPath) - flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC // TODO handle IfNoneMatch == ETag if opts.IfNoneMatch.IsWildcard() { @@ -366,13 +352,16 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin } defer f.Close() - out := NewETagWriter(f) - enc := ical.NewEncoder(out) + enc := ical.NewEncoder(f) err = enc.Encode(calendar) if err != nil { return nil, err } + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } info, err := f.Stat() if err != nil { return nil, err @@ -382,10 +371,9 @@ func (b *filesystemBackend) PutCalendarObject(ctx context.Context, objPath strin Path: objPath, ModTime: info.ModTime(), ContentLength: info.Size(), - ETag: out.ETag(), + ETag: etag, Data: calendar, } - log.Debug().Str("path", r.Path).Str("etag", r.ETag).Int64("size", info.Size()).Msg("calendar object updated") return &r, nil } @@ -396,10 +384,6 @@ func (b *filesystemBackend) DeleteCalendarObject(ctx context.Context, path strin if err != nil { return err } - - b.Lock(localPath) - defer b.Unlock(localPath) - err = os.Remove(localPath) if err != nil { return err diff --git a/storage/filesystem_carddav.go b/storage/filesystem_carddav.go index 6cdc49c..764dc10 100644 --- a/storage/filesystem_carddav.go +++ b/storage/filesystem_carddav.go @@ -68,21 +68,20 @@ func vcardPropFilter(card vcard.Card, props []string) vcard.Card { return result } -func vcardAndEtagFromFile(path string, propFilter []string) (vcard.Card, string, error) { +func vcardFromFile(path string, propFilter []string) (vcard.Card, error) { f, err := os.Open(path) if err != nil { - return nil, "", err + return nil, err } defer f.Close() - src := NewETagReader(f) - dec := vcard.NewDecoder(src) + dec := vcard.NewDecoder(f) card, err := dec.Decode() if err != nil { - return nil, "", err + return nil, err } - return vcardPropFilter(card, propFilter), src.ETag(), nil + return vcardPropFilter(card, propFilter), nil } func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath string, propFilter []string) ([]carddav.AddressObject, error) { @@ -104,10 +103,12 @@ func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath s return nil } - b.RLock(filename) - defer b.RUnlock(filename) + card, err := vcardFromFile(filename, propFilter) + if err != nil { + return err + } - card, etag, err := vcardAndEtagFromFile(filename, propFilter) + etag, err := etagForFile(filename) if err != nil { return err } @@ -121,7 +122,6 @@ func (b *filesystemBackend) loadAllAddressObjects(ctx context.Context, urlPath s ETag: etag, Card: card, } - log.Debug().Str("path", obj.Path).Str("etag", etag).Int64("size", info.Size()).Msg("address object loaded") result = append(result, obj) return nil }) @@ -141,12 +141,7 @@ func (b *filesystemBackend) writeAddressBook(ctx context.Context, ab *carddav.Ad if err != nil { return err } - - filename := path.Join(localPath, addressBookFileName) - b.Lock(filename) - defer b.Unlock(filename) - - err = os.WriteFile(filename, blob, 0644) + return os.WriteFile(path.Join(localPath, addressBookFileName), blob, 0644) if err != nil { return fmt.Errorf("error writing address book: %s", err.Error()) } @@ -213,9 +208,6 @@ func (b *filesystemBackend) ListAddressBooks(ctx context.Context) ([]carddav.Add } abPath := path.Join(filename, addressBookFileName) - b.RLock(abPath) - defer b.RUnlock(abPath) - data, err := os.ReadFile(abPath) if err != nil { if os.IsNotExist(err) { @@ -258,9 +250,6 @@ func (b *filesystemBackend) GetAddressBook(ctx context.Context, urlPath string) log.Debug().Str("path", localPath).Msg("loading addressbook") - b.RLock(localPath) - defer b.RUnlock(localPath) - data, err := os.ReadFile(localPath) if err != nil { if os.IsNotExist(err) { @@ -314,9 +303,6 @@ func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string return nil, err } - b.RLock(localPath) - defer b.RUnlock(localPath) - info, err := os.Stat(localPath) if err != nil { if errors.Is(err, fs.ErrNotExist) { @@ -330,12 +316,17 @@ func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string propFilter = req.Props } - card, etag, err := vcardAndEtagFromFile(localPath, propFilter) + card, err := vcardFromFile(localPath, propFilter) if err != nil { log.Debug().Str("path", localPath).Err(err).Msg("error reading calendar") return nil, err } + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } + obj := carddav.AddressObject{ Path: objPath, ModTime: info.ModTime(), @@ -343,7 +334,6 @@ func (b *filesystemBackend) GetAddressObject(ctx context.Context, objPath string ETag: etag, Card: card, } - log.Debug().Str("path", objPath).Str("etag", etag).Int64("size", info.Size()).Msg("returning address object") return &obj, nil } @@ -391,9 +381,6 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string return nil, err } - b.Lock(localPath) - defer b.Unlock(localPath) - flags := os.O_RDWR | os.O_CREATE | os.O_TRUNC // TODO handle IfNoneMatch == ETag if opts.IfNoneMatch.IsWildcard() { @@ -426,12 +413,15 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string } defer f.Close() - out := NewETagWriter(f) - enc := vcard.NewEncoder(out) + enc := vcard.NewEncoder(f) if err := enc.Encode(card); err != nil { return nil, err } + etag, err := etagForFile(localPath) + if err != nil { + return nil, err + } info, err := f.Stat() if err != nil { return nil, err @@ -441,10 +431,9 @@ func (b *filesystemBackend) PutAddressObject(ctx context.Context, objPath string Path: objPath, ModTime: info.ModTime(), ContentLength: info.Size(), - ETag: out.ETag(), + ETag: etag, Card: card, } - log.Debug().Str("path", r.Path).Str("etag", r.ETag).Int64("size", info.Size()).Msg("address object updated") return &r, nil } @@ -455,10 +444,6 @@ func (b *filesystemBackend) DeleteAddressObject(ctx context.Context, path string if err != nil { return err } - - b.Lock(localPath) - defer b.Unlock(localPath) - err = os.Remove(localPath) if err != nil { return err