104 lines
2.6 KiB
Go
104 lines
2.6 KiB
Go
package config
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"os"
|
|
|
|
"github.com/emersion/go-ical"
|
|
"github.com/emersion/go-webdav"
|
|
"github.com/emersion/go-webdav/caldav"
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
type Config struct {
|
|
Server string `yaml:"server"`
|
|
Username string `yaml:"username"`
|
|
Password string `yaml:"password"`
|
|
Calendars []CalendarSync `yaml:"calendars"`
|
|
}
|
|
|
|
type CalendarSync struct {
|
|
Name string `yaml:"name"`
|
|
Destination string `yaml:"destination"`
|
|
URL string `yaml:"url"`
|
|
}
|
|
|
|
func (item *CalendarSync) Sync(ctx context.Context, cfg *Config) error {
|
|
webdavClient := webdav.HTTPClientWithBasicAuth(http.DefaultClient, cfg.Username, cfg.Password)
|
|
|
|
cc, err := caldav.NewClient(webdavClient, cfg.Server)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to construct a CalDAV client: %w", err)
|
|
}
|
|
|
|
calPath, err := CalendarPath(ctx, cc, cfg.Username, item.Destination)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to find destination calendar path: %w", err)
|
|
}
|
|
|
|
resp, err := http.Get(item.URL)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to sync %s: %w", item.Name, err)
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return fmt.Errorf("source returned non-200 status code: %s", resp.Status)
|
|
}
|
|
|
|
calendarData, err := ical.NewDecoder(resp.Body).Decode()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to decode source calendar data: %w", err)
|
|
}
|
|
|
|
// if err := ical.NewEncoder(os.Stdout).Encode(calendarData); err == nil {
|
|
// os.Exit(0)
|
|
// }
|
|
|
|
if _, err := cc.PutCalendarObject(ctx, calPath, calendarData); err != nil {
|
|
return fmt.Errorf("failed to push calendar data to destination: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func CalendarPath(ctx context.Context, cc *caldav.Client, username, calendarName string) (string, error) {
|
|
homeSet, err := cc.FindCalendarHomeSet(ctx, username)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to find home set for %s: %w", username, err)
|
|
}
|
|
|
|
calendars, err := cc.FindCalendars(ctx, homeSet)
|
|
if err != nil {
|
|
return "", fmt.Errorf("failed to retrieve calendars: %w", err)
|
|
}
|
|
|
|
for _, cal := range calendars {
|
|
if cal.Name == calendarName {
|
|
return cal.Path, nil
|
|
}
|
|
}
|
|
|
|
return "", fmt.Errorf("calendar with name %s not found", calendarName)
|
|
}
|
|
|
|
// Load a configuration file
|
|
func Load(configFile string) (*Config, error) {
|
|
cfg := new(Config)
|
|
|
|
f, err := os.Open(configFile)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to open config file %s: %w", configFile, err)
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
if err = yaml.NewDecoder(f).Decode(cfg); err != nil {
|
|
return nil, fmt.Errorf("failed to decode config file %s: %w", configFile, err)
|
|
}
|
|
|
|
return cfg, nil
|
|
}
|