package caldav import ( "fmt" "log" "net/http" "strings" cent "github.com/dolanor/caldav-go/caldav/entities" "github.com/dolanor/caldav-go/icalendar/components" "github.com/dolanor/caldav-go/utils" "github.com/dolanor/caldav-go/webdav" "github.com/dolanor/caldav-go/webdav/entities" ) var _ = log.Print // a client for making WebDAV requests type Client webdav.Client // downcasts the client to the WebDAV interface func (c *Client) WebDAV() *webdav.Client { return (*webdav.Client)(c) } // returns the embedded CalDAV server reference func (c *Client) Server() *Server { return (*Server)(c.WebDAV().Server()) } // fetches a list of CalDAV features supported by the server // returns an error if the server does not support DAV func (c *Client) Features(path string) ([]string, error) { var cfeatures []string if features, err := c.WebDAV().Features(path); err != nil { return cfeatures, utils.NewError(c.Features, "unable to detect features", c, err) } else { for _, feature := range features { if strings.HasPrefix(feature, "calendar-") { cfeatures = append(cfeatures, feature) } } return cfeatures, nil } } // fetches a list of CalDAV features and checks if a certain one is supported by the server // returns an error if the server does not support DAV func (c *Client) SupportsFeature(name string, path string) (bool, error) { if features, err := c.Features(path); err != nil { return false, utils.NewError(c.SupportsFeature, "feature detection failed", c, err) } else { var test = fmt.Sprintf("calendar-%s", name) for _, feature := range features { if feature == test { return true, nil } } return false, nil } } // fetches a list of CalDAV features and checks if a certain one is supported by the server // returns an error if the server does not support DAV func (c *Client) ValidateServer(path string) error { if found, err := c.SupportsFeature("access", path); err != nil { return utils.NewError(c.SupportsFeature, "feature detection failed", c, err) } else if !found { return utils.NewError(c.SupportsFeature, "calendar access feature missing", c, nil) } else { return nil } } // creates a new calendar collection on a given path func (c *Client) MakeCalendar(path string) error { if req, err := c.Server().NewRequest("MKCALENDAR", path); err != nil { return utils.NewError(c.MakeCalendar, "unable to create request", c, err) } else if resp, err := c.Do(req); err != nil { return utils.NewError(c.MakeCalendar, "unable to execute request", c, err) } else if resp.StatusCode != http.StatusCreated { err := new(entities.Error) resp.Decode(err) msg := fmt.Sprintf("unexpected server response %s", resp.Status) return utils.NewError(c.MakeCalendar, msg, c, err) } else { return nil } } // creates or updates one or more events on the remote CalDAV server func (c *Client) PutEvents(path string, events ...*components.Event) error { if len(events) <= 0 { return utils.NewError(c.PutEvents, "no calendar events provided", c, nil) } else if cal := components.NewCalendar(events...); events[0] == nil { return utils.NewError(c.PutEvents, "icalendar event must not be nil", c, nil) } else if err := c.PutCalendars(path, cal); err != nil { return utils.NewError(c.PutEvents, "unable to put calendar", c, err) } return nil } // creates or updates one or more calendars on the remote CalDAV server func (c *Client) PutCalendars(path string, calendars ...*components.Calendar) error { if req, err := c.Server().NewRequest("PUT", path, calendars); err != nil { return utils.NewError(c.PutCalendars, "unable to encode request", c, err) } else if resp, err := c.Do(req); err != nil { return utils.NewError(c.PutCalendars, "unable to execute request", c, err) } else if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusNoContent { err := new(entities.Error) resp.WebDAV().Decode(err) msg := fmt.Sprintf("unexpected server response %s", resp.Status) return utils.NewError(c.PutCalendars, msg, c, err) } return nil } // attempts to fetch an event on the remote CalDAV server func (c *Client) GetEvents(path string) ([]*components.Event, error) { cal := new(components.Calendar) if req, err := c.Server().NewRequest("GET", path); err != nil { return nil, utils.NewError(c.GetEvents, "unable to create request", c, err) } else if resp, err := c.Do(req); err != nil { return nil, utils.NewError(c.GetEvents, "unable to execute request", c, err) } else if resp.StatusCode != http.StatusOK { err := new(entities.Error) resp.WebDAV().Decode(err) msg := fmt.Sprintf("unexpected server response %s", resp.Status) return nil, utils.NewError(c.GetEvents, msg, c, err) } else if err := resp.Decode(cal); err != nil { return nil, utils.NewError(c.GetEvents, "unable to decode response", c, err) } else { return cal.Events, nil } } // attempts to fetch an event on the remote CalDAV server func (c *Client) QueryEvents(path string, query *cent.CalendarQuery) (events []*components.Event, oerr error) { ms := new(cent.Multistatus) if req, err := c.Server().WebDAV().NewRequest("REPORT", path, query); err != nil { oerr = utils.NewError(c.QueryEvents, "unable to create request", c, err) } else if req.Http().Native().Header.Set("Depth", string(webdav.Depth1)); false { } else if resp, err := c.WebDAV().Do(req); err != nil { oerr = utils.NewError(c.QueryEvents, "unable to execute request", c, err) } else if resp.StatusCode == http.StatusNotFound { return // no events if not found } else if resp.StatusCode != webdav.StatusMulti { err := new(entities.Error) msg := fmt.Sprintf("unexpected server response %s", resp.Status) resp.Decode(err) oerr = utils.NewError(c.QueryEvents, msg, c, err) } else if err := resp.Decode(ms); err != nil { msg := "unable to decode response" oerr = utils.NewError(c.QueryEvents, msg, c, err) } else { for i, r := range ms.Responses { for j, p := range r.PropStats { if p.Prop == nil || p.Prop.CalendarData == nil { continue } else if cal, err := p.Prop.CalendarData.CalendarComponent(); err != nil { msg := fmt.Sprintf("unable to decode property %d of response %d", j, i) oerr = utils.NewError(c.QueryEvents, msg, c, err) return } else { events = append(events, cal.Events...) } } } } return } // executes a CalDAV request func (c *Client) Do(req *Request) (*Response, error) { if resp, err := c.WebDAV().Do((*webdav.Request)(req)); err != nil { return nil, utils.NewError(c.Do, "unable to execute CalDAV request", c, err) } else { return NewResponse(resp), nil } } // creates a new client for communicating with an WebDAV server func NewClient(server *Server, native *http.Client) *Client { return (*Client)(webdav.NewClient((*webdav.Server)(server), native)) } // creates a new client for communicating with a WebDAV server // uses the default HTTP client from net/http func NewDefaultClient(server *Server) *Client { return NewClient(server, http.DefaultClient) }