package nextcloud import ( "context" "fmt" "io" "net/http" "time" ) type Client struct { baseURL string httpClient *http.Client adminUser string adminPass string credStore *DAVCredentialStore } func NewClient(baseURL, adminUser, adminPass string) *Client { return &Client{ baseURL: baseURL, httpClient: &http.Client{ Timeout: 30 * time.Second, }, adminUser: adminUser, adminPass: adminPass, } } func (c *Client) WithDAVCredentials(store *DAVCredentialStore) *Client { if c == nil { return nil } c.credStore = store return c } func (c *Client) doRequest(ctx context.Context, method, path string, body io.Reader, headers map[string]string) (*http.Response, error) { url := c.baseURL + path req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } req.SetBasicAuth(c.adminUser, c.adminPass) req.Header.Set("OCS-APIRequest", "true") for k, v := range headers { req.Header.Set(k, v) } return c.httpClient.Do(req) } func (c *Client) DoAsUser(ctx context.Context, method, path string, body io.Reader, userID string, headers map[string]string) (*http.Response, error) { return c.doAsUser(ctx, method, path, body, userID, headers, false) } func (c *Client) doAsUser(ctx context.Context, method, path string, body io.Reader, userID string, headers map[string]string, retried bool) (*http.Response, error) { token, err := c.userDAVToken(ctx, userID) if err != nil { return nil, err } url := c.baseURL + path req, err := http.NewRequestWithContext(ctx, method, url, body) if err != nil { return nil, err } req.SetBasicAuth(userID, token) for k, v := range headers { req.Header.Set(k, v) } resp, err := c.httpClient.Do(req) if err != nil { return nil, err } if resp.StatusCode == http.StatusUnauthorized && c.credStore != nil { resp.Body.Close() _ = c.credStore.DeleteToken(ctx, userID) if !retried { if refreshErr := c.RefreshPrincipalCredentials(ctx, userID); refreshErr == nil { return c.doAsUser(ctx, method, path, body, userID, headers, true) } } return nil, ErrDAVCredentialsMissing } return resp, nil } func (c *Client) userDAVToken(ctx context.Context, userID string) (string, error) { if c.credStore == nil { return "", fmt.Errorf("nextcloud dav credentials store not configured") } token, err := c.credStore.GetToken(ctx, userID) if err != nil { return "", err } return token, nil }