package carddav import ( "context" "net/http" "time" "git.happydns.org/checker-dav/internal/dav" sdk "git.happydns.org/checker-sdk-go/checker" webdav "github.com/emersion/go-webdav" "github.com/emersion/go-webdav/carddav" ) func (p *carddavProvider) Collect(ctx context.Context, opts sdk.CheckerOptions) (any, error) { domain, _ := sdk.GetOption[string](opts, "domain_name") user, _ := sdk.GetOption[string](opts, "username") pass, _ := sdk.GetOption[string](opts, "password") explicit, _ := sdk.GetOption[string](opts, "context_url") timeoutSec := sdk.GetFloatOption(opts, "timeout_seconds", 10) timeout := time.Duration(timeoutSec * float64(time.Second)) if timeout <= 0 { timeout = 10 * time.Second } obs := &dav.Observation{ Kind: dav.KindCardDAV, Domain: domain, HasCredentials: user != "" && pass != "", CollectedAt: time.Now(), } anonClient := dav.NewHTTPClient(timeout) // Phase 1: Discovery obs.Discovery = dav.Discover(ctx, anonClient, dav.KindCardDAV, domain, explicit) if obs.Discovery.ContextURL == "" { return obs, nil } // Phase 2: OPTIONS optsRes, err := dav.ProbeOptions(ctx, anonClient, obs.Discovery.ContextURL) obs.Options = optsRes if err != nil { obs.Transport = dav.TransportResult{Error: err.Error()} return obs, nil } obs.Transport = dav.TransportResult{Reached: true} // Phase 3: Authenticated if !obs.HasCredentials { obs.Principal.Skipped = true obs.HomeSet.Skipped = true obs.Collections.Skipped = true obs.Report.Skipped = true return obs, nil } authClient := dav.WithBasicAuth(anonClient, obs.Discovery.ContextURL, user, pass) principal, err := dav.FindPrincipal(ctx, authClient, obs.Discovery.ContextURL) if err != nil { obs.Principal.Error = err.Error() obs.HomeSet.Skipped = true obs.Collections.Skipped = true obs.Report.Skipped = true return obs, nil } obs.Principal.URL = principal card, err := carddav.NewClient(asHTTPClient(authClient), obs.Discovery.ContextURL) if err != nil { obs.HomeSet.Error = err.Error() obs.Collections.Skipped = true obs.Report.Skipped = true return obs, nil } home, err := card.FindAddressBookHomeSet(ctx, principal) if err != nil { obs.HomeSet.Error = err.Error() obs.Collections.Skipped = true obs.Report.Skipped = true return obs, nil } obs.HomeSet.URL = home books, err := card.FindAddressBooks(ctx, home) if err != nil { obs.Collections.Error = err.Error() obs.Report.Skipped = true } else { for _, b := range books { item := dav.CollectionInfo{ Path: b.Path, Name: b.Name, Description: b.Description, MaxResourceSize: b.MaxResourceSize, } for _, d := range b.SupportedAddressData { item.SupportedAddressData = append(item.SupportedAddressData, d.ContentType+";"+d.Version) } obs.Collections.Items = append(obs.Collections.Items, item) } } if len(obs.Collections.Items) > 0 { first := obs.Collections.Items[0].Path obs.Report.ProbePath = first q := &carddav.AddressBookQuery{ DataRequest: carddav.AddressDataRequest{AllProp: true}, Limit: 1, } if _, err := card.QueryAddressBook(ctx, first, q); err != nil { obs.Report.Error = err.Error() } else { obs.Report.QueryOK = true } } else { obs.Report.Skipped = true } return obs, nil } func asHTTPClient(c *http.Client) webdav.HTTPClient { return c }