checker-dav/internal/dav/options_shared.go
Pierre-Olivier Mercier 7d5535fddf Initial commit
CalDAV and CardDAV checkers sharing a single Go module. Discovery follows
RFC 6764 (/.well-known + SRV/TXT), authenticated probes cover principal,
home-set, collections and a minimal REPORT query on top of go-webdav.
Common shape in internal/dav/; CalDAV adds a scheduling rule.

Surfaces its context URL (and each secure-SRV target) as TLS endpoints via
the EndpointDiscoverer interface, so the dedicated TLS checker can pick
them up without re-parsing observations.

HTML report foregrounds common misconfigs (well-known returning 200,
missing SRV, plaintext-only SRV, missing DAV capability, skipped auth
phase) as action-item callouts before the full phase breakdown.
2026-04-26 21:47:40 +07:00

125 lines
3.3 KiB
Go

package dav
import (
"errors"
"net/http"
"strconv"
"strings"
sdk "git.happydns.org/checker-sdk-go/checker"
)
func UserOptions() []sdk.CheckerOptionDocumentation {
return []sdk.CheckerOptionDocumentation{
{
Id: "username",
Type: "string",
Label: "Username",
Description: "Optional. Supplying credentials unlocks authenticated checks (principal, home-set, collections, report probe).",
},
{
Id: "password",
Type: "string",
Label: "Password or token",
Description: "Optional. Paired with the username for HTTP Basic authentication.",
Secret: true,
},
{
Id: "context_url",
Type: "string",
Label: "Explicit context URL",
Description: "Optional. Bypasses /.well-known and SRV discovery. Use for servers with a non-standard layout.",
Placeholder: "https://dav.example.com/caldav/",
},
}
}
func DomainOptions() []sdk.CheckerOptionDocumentation {
return []sdk.CheckerOptionDocumentation{
{
Id: "domain_name",
Label: "Domain name",
AutoFill: sdk.AutoFillDomainName,
},
}
}
func RunOptions() []sdk.CheckerOptionDocumentation {
return []sdk.CheckerOptionDocumentation{
{
Id: "timeout_seconds",
Type: "number",
Label: "Timeout (seconds)",
Description: "Per-request HTTP timeout.",
Default: float64(10),
},
}
}
// InteractiveForm mirrors UserOptions+DomainOptions+RunOptions for the
// standalone /check page. Discovery happens inside Collect, so all the
// human owes us is the domain.
func InteractiveForm() []sdk.CheckerOptionField {
return []sdk.CheckerOptionField{
{
Id: "domain_name",
Type: "string",
Label: "Domain name",
Placeholder: "example.com",
Required: true,
},
{
Id: "username",
Type: "string",
Label: "Username",
Description: "Optional. Supplying credentials unlocks authenticated checks.",
},
{
Id: "password",
Type: "string",
Label: "Password or token",
Description: "Optional. Paired with the username for HTTP Basic auth.",
Secret: true,
},
{
Id: "context_url",
Type: "string",
Label: "Explicit context URL",
Description: "Optional. Bypasses /.well-known and SRV discovery.",
Placeholder: "https://dav.example.com/caldav/",
},
{
Id: "timeout_seconds",
Type: "number",
Label: "Timeout (seconds)",
Description: "Per-request HTTP timeout.",
Default: float64(10),
},
}
}
func ParseInteractiveForm(r *http.Request) (sdk.CheckerOptions, error) {
domain := strings.TrimSpace(r.FormValue("domain_name"))
if domain == "" {
return nil, errors.New("domain name is required")
}
opts := sdk.CheckerOptions{"domain_name": domain}
if v := strings.TrimSpace(r.FormValue("username")); v != "" {
opts["username"] = v
}
if v := r.FormValue("password"); v != "" {
opts["password"] = v
}
if v := strings.TrimSpace(r.FormValue("context_url")); v != "" {
opts["context_url"] = v
}
if v := strings.TrimSpace(r.FormValue("timeout_seconds")); v != "" {
f, err := strconv.ParseFloat(v, 64)
if err != nil {
return nil, errors.New("timeout must be a number")
}
opts["timeout_seconds"] = f
}
return opts, nil
}