vendoring and caldav
This commit is contained in:
parent
a06aa900b7
commit
fd9092e5ab
96 changed files with 14832 additions and 5 deletions
277
vendor/github.com/samedi/caldav-go/data/resource.go
generated
vendored
Normal file
277
vendor/github.com/samedi/caldav-go/data/resource.go
generated
vendored
Normal file
|
@ -0,0 +1,277 @@
|
|||
package data
|
||||
|
||||
import (
|
||||
"os"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
"strings"
|
||||
"strconv"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/laurent22/ical-go/ical"
|
||||
|
||||
"github.com/samedi/caldav-go/lib"
|
||||
"github.com/samedi/caldav-go/files"
|
||||
)
|
||||
|
||||
type ResourceInterface interface {
|
||||
ComponentName() string
|
||||
StartTimeUTC() time.Time
|
||||
EndTimeUTC() time.Time
|
||||
Recurrences() []ResourceRecurrence
|
||||
HasProperty(propPath... string) bool
|
||||
GetPropertyValue(propPath... string) string
|
||||
HasPropertyParam(paramName... string) bool
|
||||
GetPropertyParamValue(paramName... string) string
|
||||
}
|
||||
|
||||
type ResourceAdapter interface {
|
||||
IsCollection() bool
|
||||
CalculateEtag() string
|
||||
GetContent() string
|
||||
GetContentSize() int64
|
||||
GetModTime() time.Time
|
||||
}
|
||||
|
||||
type ResourceRecurrence struct {
|
||||
StartTime time.Time
|
||||
EndTime time.Time
|
||||
}
|
||||
|
||||
type Resource struct {
|
||||
Name string
|
||||
Path string
|
||||
|
||||
pathSplit []string
|
||||
adapter ResourceAdapter
|
||||
|
||||
emptyTime time.Time
|
||||
}
|
||||
|
||||
func NewResource(resPath string, adp ResourceAdapter) Resource {
|
||||
pClean := lib.ToSlashPath(resPath)
|
||||
pSplit := strings.Split(strings.Trim(pClean, "/"), "/")
|
||||
|
||||
return Resource {
|
||||
Name: pSplit[len(pSplit) - 1],
|
||||
Path: pClean,
|
||||
pathSplit: pSplit,
|
||||
adapter: adp,
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) IsCollection() bool {
|
||||
return r.adapter.IsCollection()
|
||||
}
|
||||
|
||||
func (r *Resource) IsPrincipal() bool {
|
||||
return len(r.pathSplit) <= 1
|
||||
}
|
||||
|
||||
func (r *Resource) ComponentName() string {
|
||||
if r.IsCollection() {
|
||||
return lib.VCALENDAR
|
||||
} else {
|
||||
return lib.VEVENT
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) StartTimeUTC() time.Time {
|
||||
vevent := r.icalVEVENT()
|
||||
dtstart := vevent.PropDate(ical.DTSTART, r.emptyTime)
|
||||
|
||||
if dtstart == r.emptyTime {
|
||||
log.Printf("WARNING: The property DTSTART was not found in the resource's ical data.\nResource path: %s", r.Path)
|
||||
return r.emptyTime
|
||||
}
|
||||
|
||||
return dtstart.UTC()
|
||||
}
|
||||
|
||||
func (r *Resource) EndTimeUTC() time.Time {
|
||||
vevent := r.icalVEVENT()
|
||||
dtend := vevent.PropDate(ical.DTEND, r.emptyTime)
|
||||
|
||||
// when the DTEND property is not present, we just add the DURATION (if any) to the DTSTART
|
||||
if dtend == r.emptyTime {
|
||||
duration := vevent.PropDuration(ical.DURATION)
|
||||
dtend = r.StartTimeUTC().Add(duration)
|
||||
}
|
||||
|
||||
return dtend.UTC()
|
||||
}
|
||||
|
||||
func (r *Resource) Recurrences() []ResourceRecurrence {
|
||||
// TODO: Implement. This server does not support ical recurrences yet. We just return an empty array.
|
||||
return []ResourceRecurrence{}
|
||||
}
|
||||
|
||||
func (r *Resource) HasProperty(propPath... string) bool {
|
||||
return r.GetPropertyValue(propPath...) != ""
|
||||
}
|
||||
|
||||
func (r *Resource) GetPropertyValue(propPath... string) string {
|
||||
if propPath[0] == ical.VCALENDAR {
|
||||
propPath = propPath[1:]
|
||||
}
|
||||
|
||||
prop, _ := r.icalendar().DigProperty(propPath...)
|
||||
return prop
|
||||
}
|
||||
|
||||
func (r *Resource) HasPropertyParam(paramPath... string) bool {
|
||||
return r.GetPropertyParamValue(paramPath...) != ""
|
||||
}
|
||||
|
||||
func (r *Resource) GetPropertyParamValue(paramPath... string) string {
|
||||
if paramPath[0] == ical.VCALENDAR {
|
||||
paramPath = paramPath[1:]
|
||||
}
|
||||
|
||||
param, _ := r.icalendar().DigParameter(paramPath...)
|
||||
return param
|
||||
}
|
||||
|
||||
func (r *Resource) GetEtag() (string, bool) {
|
||||
if r.IsCollection() {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return r.adapter.CalculateEtag(), true
|
||||
}
|
||||
|
||||
func (r *Resource) GetContentType() (string, bool) {
|
||||
if r.IsCollection() {
|
||||
return "text/calendar", true
|
||||
} else {
|
||||
return "text/calendar; component=vcalendar", true
|
||||
}
|
||||
}
|
||||
|
||||
func (r *Resource) GetDisplayName() (string, bool) {
|
||||
return r.Name, true
|
||||
}
|
||||
|
||||
func (r *Resource) GetContentData() (string, bool) {
|
||||
data := r.adapter.GetContent()
|
||||
found := data != ""
|
||||
|
||||
return data, found
|
||||
}
|
||||
|
||||
func (r *Resource) GetContentLength() (string, bool) {
|
||||
// If its collection, it does not have any content, so mark it as not found
|
||||
if r.IsCollection() {
|
||||
return "", false
|
||||
}
|
||||
|
||||
contentSize := r.adapter.GetContentSize()
|
||||
return strconv.FormatInt(contentSize, 10), true
|
||||
}
|
||||
|
||||
func (r *Resource) GetLastModified(format string) (string, bool) {
|
||||
return r.adapter.GetModTime().Format(format), true
|
||||
}
|
||||
|
||||
func (r *Resource) GetOwner() (string, bool) {
|
||||
var owner string
|
||||
if len(r.pathSplit) > 1 {
|
||||
owner = r.pathSplit[0]
|
||||
} else {
|
||||
owner = ""
|
||||
}
|
||||
|
||||
return owner, true
|
||||
}
|
||||
|
||||
func (r *Resource) GetOwnerPath() (string, bool) {
|
||||
owner, _ := r.GetOwner()
|
||||
|
||||
if owner != "" {
|
||||
return fmt.Sprintf("/%s/", owner), true
|
||||
} else {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: memoize
|
||||
func (r *Resource) icalVEVENT() *ical.Node {
|
||||
vevent := r.icalendar().ChildByName(ical.VEVENT)
|
||||
|
||||
// if nil, log it and return an empty vevent
|
||||
if vevent == nil {
|
||||
log.Printf("WARNING: The resource's ical data is missing the VEVENT property.\nResource path: %s", r.Path)
|
||||
|
||||
return &ical.Node{
|
||||
Name: ical.VEVENT,
|
||||
}
|
||||
}
|
||||
|
||||
return vevent
|
||||
}
|
||||
|
||||
// TODO: memoize
|
||||
func (r *Resource) icalendar() *ical.Node {
|
||||
data, found := r.GetContentData()
|
||||
|
||||
if !found {
|
||||
log.Printf("WARNING: The resource's ical data does not have any data.\nResource path: %s", r.Path)
|
||||
return &ical.Node{
|
||||
Name: ical.VCALENDAR,
|
||||
}
|
||||
}
|
||||
|
||||
icalNode, err := ical.ParseCalendar(data)
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Could not parse the resource's ical data.\nError: %s.\nResource path: %s", err, r.Path)
|
||||
return &ical.Node{
|
||||
Name: ical.VCALENDAR,
|
||||
}
|
||||
}
|
||||
|
||||
return icalNode
|
||||
}
|
||||
|
||||
type FileResourceAdapter struct {
|
||||
finfo os.FileInfo
|
||||
resourcePath string
|
||||
}
|
||||
|
||||
func (adp *FileResourceAdapter) IsCollection() bool {
|
||||
return adp.finfo.IsDir()
|
||||
}
|
||||
|
||||
func (adp *FileResourceAdapter) GetContent() string {
|
||||
if adp.IsCollection() {
|
||||
return ""
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadFile(files.AbsPath(adp.resourcePath))
|
||||
if err != nil {
|
||||
log.Printf("ERROR: Could not read file content for the resource.\nError: %s.\nResource path: %s.", err, adp.resourcePath)
|
||||
return ""
|
||||
}
|
||||
|
||||
return string(data)
|
||||
}
|
||||
|
||||
func (adp *FileResourceAdapter) GetContentSize() int64 {
|
||||
return adp.finfo.Size()
|
||||
}
|
||||
|
||||
func (adp *FileResourceAdapter) CalculateEtag() string {
|
||||
// returns ETag as the concatenated hex values of a file's
|
||||
// modification time and size. This is not a reliable synchronization
|
||||
// mechanism for directories, so for collections we return empty.
|
||||
if adp.IsCollection() {
|
||||
return ""
|
||||
}
|
||||
|
||||
fi := adp.finfo
|
||||
return fmt.Sprintf(`"%x%x"`, fi.ModTime().UnixNano(), fi.Size())
|
||||
}
|
||||
|
||||
func (adp *FileResourceAdapter) GetModTime() time.Time {
|
||||
return adp.finfo.ModTime()
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue