// Package dav holds code shared by the CalDAV and CardDAV checkers: // discovery, OPTIONS probing, PROPFIND helpers, and report rendering. package dav import "time" // Kind is carried end-to-end through a run so shared helpers branch on it // rather than duplicating per-protocol code. type Kind string const ( KindCalDAV Kind = "caldav" KindCardDAV Kind = "carddav" ) // ServiceName returns the RFC 6764 SRV label, with the leading "_" but // without the "_tcp" suffix. func (k Kind) ServiceName(secure bool) string { switch k { case KindCalDAV: if secure { return "_caldavs" } return "_caldav" case KindCardDAV: if secure { return "_carddavs" } return "_carddav" } return "" } func (k Kind) WellKnownPath() string { return "/.well-known/" + string(k) } // RequiredCapability is the DAV: header token a compliant server must // advertise. func (k Kind) RequiredCapability() string { switch k { case KindCalDAV: return "calendar-access" case KindCardDAV: return "addressbook" } return "" } // Observation is what each checker persists. Scheduling is CalDAV-only and // left nil for CardDAV. type Observation struct { Kind Kind `json:"kind"` Domain string `json:"domain"` HasCredentials bool `json:"has_credentials"` Discovery DiscoveryResult `json:"discovery"` Transport TransportResult `json:"transport"` Options OptionsResult `json:"options"` Principal PrincipalResult `json:"principal"` HomeSet HomeSetResult `json:"home_set"` Collections CollectionsResult `json:"collections"` Report ReportResult `json:"report"` Scheduling *SchedulingResult `json:"scheduling,omitempty"` CollectedAt time.Time `json:"collected_at"` } type SRVRecord struct { Target string `json:"target"` Port uint16 `json:"port"` Priority uint16 `json:"priority"` Weight uint16 `json:"weight"` } // DiscoveryResult records every signal seen during lookup, even on failure, // so the report can pinpoint which leg of discovery broke. type DiscoveryResult struct { SecureSRV []SRVRecord `json:"secure_srv,omitempty"` PlaintextSRV []SRVRecord `json:"plaintext_srv,omitempty"` SRVError string `json:"srv_error,omitempty"` TXTPath string `json:"txt_path,omitempty"` TXTError string `json:"txt_error,omitempty"` WellKnownURL string `json:"well_known_url,omitempty"` WellKnownCode int `json:"well_known_code,omitempty"` WellKnownChain []string `json:"well_known_chain,omitempty"` WellKnownError string `json:"well_known_error,omitempty"` ContextURL string `json:"context_url,omitempty"` Source string `json:"source,omitempty"` // "explicit", "well-known", "srv-txt" Error string `json:"error,omitempty"` } // TransportResult is intentionally minimal: cert validation is out of scope // here, a dedicated TLS checker owns it. type TransportResult struct { Reached bool `json:"reached"` Error string `json:"error,omitempty"` } type OptionsResult struct { StatusCode int `json:"status_code"` DAVClasses []string `json:"dav_classes,omitempty"` AllowMethods []string `json:"allow_methods,omitempty"` AuthSchemes []string `json:"auth_schemes,omitempty"` Server string `json:"server,omitempty"` Error string `json:"error,omitempty"` } // PrincipalResult.Skipped is set when no credentials were supplied; the // rule turns that into StatusUnknown rather than a failure. type PrincipalResult struct { Skipped bool `json:"skipped,omitempty"` URL string `json:"url,omitempty"` Error string `json:"error,omitempty"` } type HomeSetResult struct { Skipped bool `json:"skipped,omitempty"` URL string `json:"url,omitempty"` Error string `json:"error,omitempty"` } type CollectionInfo struct { Path string `json:"path"` Name string `json:"name,omitempty"` Description string `json:"description,omitempty"` MaxResourceSize int64 `json:"max_resource_size,omitempty"` SupportedComponentSet []string `json:"supported_component_set,omitempty"` // CalDAV only SupportedAddressData []string `json:"supported_address_data,omitempty"` // CardDAV only } type CollectionsResult struct { Skipped bool `json:"skipped,omitempty"` Items []CollectionInfo `json:"items,omitempty"` Error string `json:"error,omitempty"` } type ReportResult struct { Skipped bool `json:"skipped,omitempty"` QueryOK bool `json:"query_ok,omitempty"` ProbePath string `json:"probe_path,omitempty"` Error string `json:"error,omitempty"` } // SchedulingResult is CalDAV-only. type SchedulingResult struct { Advertised bool `json:"advertised"` InboxURL string `json:"inbox_url,omitempty"` OutboxURL string `json:"outbox_url,omitempty"` Error string `json:"error,omitempty"` }