package main import ( "bytes" "encoding/xml" "fmt" "io" "net/http" "net/http/cookiejar" "strings" "text/template" "time" ) type FolderId struct { Id string `xml:",attr"` ChangeKey string `xml:",attr"` } type CalendarItem struct { Subject string UID string Start time.Time End time.Time } type EWSCalendar struct { httpClient *http.Client url string username string password string } func NewEWSCalendar(url, username, password string) *EWSCalendar { // Prepare a cookie jar, since EWS uses some. jar, err := cookiejar.New(nil) if err != nil { panic(err) } return &EWSCalendar{ httpClient: &http.Client{ Jar: jar, }, url: url, username: username, password: password, } } func (e *EWSCalendar) prepareRequest(body io.Reader) (req *http.Request, err error) { req, err = http.NewRequest(http.MethodPost, e.url, body) req.Header.Set("Content-Type", "text/xml") req.Header.Set("Accept", "text/xml") req.SetBasicAuth(e.username, e.password) return } func (e *EWSCalendar) getCalendarFolderID() (id *FolderId, err error) { req, err := e.prepareRequest(strings.NewReader(folderIdRequest)) if err != nil { return } resp, err := e.httpClient.Do(req) if err != nil { return } if resp.StatusCode != http.StatusOK { err = fmt.Errorf("unexpected status when querying folderID: %d", resp.StatusCode) return } d := xml.NewDecoder(resp.Body) defer resp.Body.Close() for { var t xml.Token t, err = d.Token() if err == io.EOF { err = nil return } else if err != nil { return } switch t := t.(type) { case xml.StartElement: if t.Name.Local == "FolderId" { id = &FolderId{} err = d.DecodeElement(id, &t) // The first one is enough return } } } } func (e *EWSCalendar) getCalendarItems(folder *FolderId, start, end time.Time) (items []CalendarItem, err error) { b := &bytes.Buffer{} model := struct { FolderId *FolderId StartDate string EndDate string }{folder, start.Format(time.RFC3339), end.Format(time.RFC3339)} err = calendarQuery.Execute(b, &model) if err != nil { return } req, err := e.prepareRequest(b) if err != nil { return } resp, err := e.httpClient.Do(req) if err != nil { return } if resp.StatusCode != http.StatusOK { err = fmt.Errorf("unexpected status when querying calendar items: %d", resp.StatusCode) return } d := xml.NewDecoder(resp.Body) defer resp.Body.Close() for { var t xml.Token t, err = d.Token() if err == io.EOF { err = nil return } else if err != nil { return } switch t := t.(type) { case xml.StartElement: if t.Name.Local == "CalendarItem" { item := CalendarItem{} err = d.DecodeElement(&item, &t) items = append(items, item) } } } return } const folderIdRequest = ` AllProperties ` var calendarQuery = template.Must(template.New("calendarQuery").Parse(` IdOnly `))