Better caldav support (#73)
This commit is contained in:
17
vendor/github.com/laurent22/ical-go/ical/calendar.go
generated
vendored
Normal file
17
vendor/github.com/laurent22/ical-go/ical/calendar.go
generated
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
package ical
|
||||
|
||||
type Calendar struct {
|
||||
Items []CalendarEvent
|
||||
}
|
||||
|
||||
func (this *Calendar) Serialize() string {
|
||||
serializer := calSerializer{
|
||||
calendar: this,
|
||||
buffer: new(strBuffer),
|
||||
}
|
||||
return serializer.serialize()
|
||||
}
|
||||
|
||||
func (this *Calendar) ToICS() string {
|
||||
return this.Serialize()
|
||||
}
|
50
vendor/github.com/laurent22/ical-go/ical/calendar_event.go
generated
vendored
Normal file
50
vendor/github.com/laurent22/ical-go/ical/calendar_event.go
generated
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type CalendarEvent struct {
|
||||
Id string
|
||||
Summary string
|
||||
Description string
|
||||
Location string
|
||||
CreatedAtUTC *time.Time
|
||||
ModifiedAtUTC *time.Time
|
||||
StartAt *time.Time
|
||||
EndAt *time.Time
|
||||
}
|
||||
|
||||
func (this *CalendarEvent) StartAtUTC() *time.Time {
|
||||
return inUTC(this.StartAt)
|
||||
}
|
||||
|
||||
func (this *CalendarEvent) EndAtUTC() *time.Time {
|
||||
return inUTC(this.EndAt)
|
||||
}
|
||||
|
||||
func (this *CalendarEvent) Serialize() string {
|
||||
buffer := new(strBuffer)
|
||||
return this.serializeWithBuffer(buffer)
|
||||
}
|
||||
|
||||
func (this *CalendarEvent) ToICS() string {
|
||||
return this.Serialize()
|
||||
}
|
||||
|
||||
func (this *CalendarEvent) serializeWithBuffer(buffer *strBuffer) string {
|
||||
serializer := calEventSerializer{
|
||||
event: this,
|
||||
buffer: buffer,
|
||||
}
|
||||
return serializer.serialize()
|
||||
}
|
||||
|
||||
func inUTC(t *time.Time) *time.Time {
|
||||
if t == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
tUTC := t.UTC()
|
||||
return &tUTC
|
||||
}
|
18
vendor/github.com/laurent22/ical-go/ical/lib.go
generated
vendored
Normal file
18
vendor/github.com/laurent22/ical-go/ical/lib.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
)
|
||||
|
||||
type strBuffer struct {
|
||||
buffer bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *strBuffer) Write(format string, elem ...interface{}) {
|
||||
b.buffer.WriteString(fmt.Sprintf(format, elem...))
|
||||
}
|
||||
|
||||
func (b *strBuffer) String() string {
|
||||
return b.buffer.String()
|
||||
}
|
168
vendor/github.com/laurent22/ical-go/ical/node.go
generated
vendored
Normal file
168
vendor/github.com/laurent22/ical-go/ical/node.go
generated
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"time"
|
||||
"regexp"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
type Node struct {
|
||||
Name string
|
||||
Value string
|
||||
Type int // 1 = Object, 0 = Name/Value
|
||||
Parameters map[string]string
|
||||
Children []*Node
|
||||
}
|
||||
|
||||
func (this *Node) ChildrenByName(name string) []*Node {
|
||||
var output []*Node
|
||||
for _, child := range this.Children {
|
||||
if child.Name == name {
|
||||
output = append(output, child)
|
||||
}
|
||||
}
|
||||
return output
|
||||
}
|
||||
|
||||
func (this *Node) ChildByName(name string) *Node {
|
||||
for _, child := range this.Children {
|
||||
if child.Name == name {
|
||||
return child
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (this *Node) PropString(name string, defaultValue string) string {
|
||||
for _, child := range this.Children {
|
||||
if child.Name == name {
|
||||
return child.Value
|
||||
}
|
||||
}
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
func (this *Node) PropDate(name string, defaultValue time.Time) time.Time {
|
||||
node := this.ChildByName(name)
|
||||
if node == nil { return defaultValue }
|
||||
tzid := node.Parameter("TZID", "")
|
||||
var output time.Time
|
||||
var err error
|
||||
if tzid != "" {
|
||||
loc, err := time.LoadLocation(tzid)
|
||||
if err != nil { panic(err) }
|
||||
output, err = time.ParseInLocation("20060102T150405", node.Value, loc)
|
||||
} else {
|
||||
output, err = time.Parse("20060102T150405Z", node.Value)
|
||||
}
|
||||
|
||||
if err != nil { panic(err) }
|
||||
return output
|
||||
}
|
||||
|
||||
func (this *Node) PropDuration(name string) time.Duration {
|
||||
durStr := this.PropString(name, "")
|
||||
|
||||
if durStr == "" {
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
durRgx := regexp.MustCompile("PT(?:([0-9]+)H)?(?:([0-9]+)M)?(?:([0-9]+)S)?")
|
||||
matches := durRgx.FindStringSubmatch(durStr)
|
||||
|
||||
if len(matches) != 4 {
|
||||
return time.Duration(0)
|
||||
}
|
||||
|
||||
strToDuration := func(value string) time.Duration {
|
||||
d := 0
|
||||
if value != "" {
|
||||
d, _ = strconv.Atoi(value)
|
||||
}
|
||||
return time.Duration(d)
|
||||
}
|
||||
|
||||
hours := strToDuration(matches[1])
|
||||
min := strToDuration(matches[2])
|
||||
sec := strToDuration(matches[3])
|
||||
|
||||
return hours * time.Hour + min * time.Minute + sec * time.Second
|
||||
}
|
||||
|
||||
func (this *Node) PropInt(name string, defaultValue int) int {
|
||||
n := this.PropString(name, "")
|
||||
if n == "" { return defaultValue }
|
||||
output, err := strconv.Atoi(n)
|
||||
if err != nil { panic(err) }
|
||||
return output
|
||||
}
|
||||
|
||||
func (this *Node) DigProperty(propPath... string) (string, bool) {
|
||||
return this.dig("prop", propPath...)
|
||||
}
|
||||
|
||||
func (this *Node) Parameter(name string, defaultValue string) string {
|
||||
if len(this.Parameters) <= 0 { return defaultValue }
|
||||
v, ok := this.Parameters[name]
|
||||
if !ok { return defaultValue }
|
||||
return v
|
||||
}
|
||||
|
||||
func (this *Node) DigParameter(paramPath... string) (string, bool) {
|
||||
return this.dig("param", paramPath...)
|
||||
}
|
||||
|
||||
// Digs a value based on a given value path.
|
||||
// valueType: can be "param" or "prop".
|
||||
// valuePath: the path to access the value.
|
||||
// Returns ("", false) when not found or (value, true) when found.
|
||||
//
|
||||
// Example:
|
||||
// dig("param", "VCALENDAR", "VEVENT", "DTEND", "TYPE") -> It will search for "VCALENDAR" node,
|
||||
// then a "VEVENT" node, then a "DTEND" note, then finally the "TYPE" param.
|
||||
func (this *Node) dig(valueType string, valuePath... string) (string, bool) {
|
||||
current := this
|
||||
lastIndex := len(valuePath) - 1
|
||||
for _, v := range valuePath[:lastIndex] {
|
||||
current = current.ChildByName(v)
|
||||
|
||||
if current == nil {
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
target := valuePath[lastIndex]
|
||||
|
||||
value := ""
|
||||
if valueType == "param" {
|
||||
value = current.Parameter(target, "")
|
||||
} else if valueType == "prop" {
|
||||
value = current.PropString(target, "")
|
||||
}
|
||||
|
||||
if value == "" {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return value, true
|
||||
}
|
||||
|
||||
func (this *Node) String() string {
|
||||
s := ""
|
||||
if this.Type == 1 {
|
||||
s += "===== " + this.Name
|
||||
s += "\n"
|
||||
} else {
|
||||
s += this.Name
|
||||
s += ":" + this.Value
|
||||
s += "\n"
|
||||
}
|
||||
for _, child := range this.Children {
|
||||
s += child.String()
|
||||
}
|
||||
if this.Type == 1 {
|
||||
s += "===== /" + this.Name
|
||||
s += "\n"
|
||||
}
|
||||
return s
|
||||
}
|
106
vendor/github.com/laurent22/ical-go/ical/parsers.go
generated
vendored
Normal file
106
vendor/github.com/laurent22/ical-go/ical/parsers.go
generated
vendored
Normal file
@ -0,0 +1,106 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"log"
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func ParseCalendar(data string) (*Node, error) {
|
||||
r := regexp.MustCompile("([\r|\t| ]*\n[\r|\t| ]*)+")
|
||||
lines := r.Split(strings.TrimSpace(data), -1)
|
||||
node, _, err, _ := parseCalendarNode(lines, 0)
|
||||
|
||||
return node, err
|
||||
}
|
||||
|
||||
func parseCalendarNode(lines []string, lineIndex int) (*Node, bool, error, int) {
|
||||
line := strings.TrimSpace(lines[lineIndex])
|
||||
_ = log.Println
|
||||
colonIndex := strings.Index(line, ":")
|
||||
if colonIndex <= 0 {
|
||||
return nil, false, errors.New("Invalid value/pair: " + line), lineIndex + 1
|
||||
}
|
||||
name := line[0:colonIndex]
|
||||
splitted := strings.Split(name, ";")
|
||||
var parameters map[string]string
|
||||
if len(splitted) >= 2 {
|
||||
name = splitted[0]
|
||||
parameters = make(map[string]string)
|
||||
for i := 1; i < len(splitted); i++ {
|
||||
p := strings.Split(splitted[i], "=")
|
||||
if len(p) != 2 { panic("Invalid parameter format: " + name) }
|
||||
parameters[p[0]] = p[1]
|
||||
}
|
||||
}
|
||||
value := line[colonIndex+1:len(line)]
|
||||
|
||||
if name == "BEGIN" {
|
||||
node := new(Node)
|
||||
node.Name = value
|
||||
node.Type = 1
|
||||
lineIndex = lineIndex + 1
|
||||
for {
|
||||
child, finished, _, newLineIndex := parseCalendarNode(lines, lineIndex)
|
||||
if finished {
|
||||
return node, false, nil, newLineIndex
|
||||
} else {
|
||||
if child != nil {
|
||||
node.Children = append(node.Children, child)
|
||||
}
|
||||
lineIndex = newLineIndex
|
||||
}
|
||||
}
|
||||
} else if name == "END" {
|
||||
return nil, true, nil, lineIndex + 1
|
||||
} else {
|
||||
node := new(Node)
|
||||
node.Name = name
|
||||
if name == "DESCRIPTION" || name == "SUMMARY" {
|
||||
text, newLineIndex := parseTextType(lines, lineIndex)
|
||||
node.Value = text
|
||||
node.Parameters = parameters
|
||||
return node, false, nil, newLineIndex
|
||||
} else {
|
||||
node.Value = value
|
||||
node.Parameters = parameters
|
||||
return node, false, nil, lineIndex + 1
|
||||
}
|
||||
}
|
||||
|
||||
panic("Unreachable")
|
||||
return nil, false, nil, lineIndex + 1
|
||||
}
|
||||
|
||||
func parseTextType(lines []string, lineIndex int) (string, int) {
|
||||
line := lines[lineIndex]
|
||||
colonIndex := strings.Index(line, ":")
|
||||
output := strings.TrimSpace(line[colonIndex+1:len(line)])
|
||||
lineIndex++
|
||||
for {
|
||||
line := lines[lineIndex]
|
||||
if line == "" || line[0] != ' ' {
|
||||
return unescapeTextType(output), lineIndex
|
||||
}
|
||||
output += line[1:len(line)]
|
||||
lineIndex++
|
||||
}
|
||||
return unescapeTextType(output), lineIndex
|
||||
}
|
||||
|
||||
func escapeTextType(input string) string {
|
||||
output := strings.Replace(input, "\\", "\\\\", -1)
|
||||
output = strings.Replace(output, ";", "\\;", -1)
|
||||
output = strings.Replace(output, ",", "\\,", -1)
|
||||
output = strings.Replace(output, "\n", "\\n", -1)
|
||||
return output
|
||||
}
|
||||
|
||||
func unescapeTextType(s string) string {
|
||||
s = strings.Replace(s, "\\;", ";", -1)
|
||||
s = strings.Replace(s, "\\,", ",", -1)
|
||||
s = strings.Replace(s, "\\n", "\n", -1)
|
||||
s = strings.Replace(s, "\\\\", "\\", -1)
|
||||
return s
|
||||
}
|
9
vendor/github.com/laurent22/ical-go/ical/properties.go
generated
vendored
Normal file
9
vendor/github.com/laurent22/ical-go/ical/properties.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
package ical
|
||||
|
||||
const (
|
||||
VCALENDAR = "VCALENDAR"
|
||||
VEVENT = "VEVENT"
|
||||
DTSTART = "DTSTART"
|
||||
DTEND = "DTEND"
|
||||
DURATION = "DURATION"
|
||||
)
|
121
vendor/github.com/laurent22/ical-go/ical/serializers.go
generated
vendored
Normal file
121
vendor/github.com/laurent22/ical-go/ical/serializers.go
generated
vendored
Normal file
@ -0,0 +1,121 @@
|
||||
package ical
|
||||
|
||||
import (
|
||||
"time"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type calSerializer struct {
|
||||
calendar *Calendar
|
||||
buffer *strBuffer
|
||||
}
|
||||
|
||||
func (this *calSerializer) serialize() string {
|
||||
this.serializeCalendar()
|
||||
return strings.TrimSpace(this.buffer.String())
|
||||
}
|
||||
|
||||
func (this *calSerializer) serializeCalendar() {
|
||||
this.begin()
|
||||
this.version()
|
||||
this.items()
|
||||
this.end()
|
||||
}
|
||||
|
||||
func (this *calSerializer) begin() {
|
||||
this.buffer.Write("BEGIN:VCALENDAR\n")
|
||||
}
|
||||
|
||||
func (this *calSerializer) end() {
|
||||
this.buffer.Write("END:VCALENDAR\n")
|
||||
}
|
||||
|
||||
func (this *calSerializer) version() {
|
||||
this.buffer.Write("VERSION:2.0\n")
|
||||
}
|
||||
|
||||
func (this *calSerializer) items() {
|
||||
for _, item := range this.calendar.Items {
|
||||
item.serializeWithBuffer(this.buffer)
|
||||
}
|
||||
}
|
||||
|
||||
type calEventSerializer struct {
|
||||
event *CalendarEvent
|
||||
buffer *strBuffer
|
||||
}
|
||||
|
||||
const (
|
||||
eventSerializerTimeFormat = "20060102T150405Z"
|
||||
)
|
||||
|
||||
func (this *calEventSerializer) serialize() string {
|
||||
this.serializeEvent()
|
||||
return strings.TrimSpace(this.buffer.String())
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) serializeEvent() {
|
||||
this.begin()
|
||||
this.uid()
|
||||
this.created()
|
||||
this.lastModified()
|
||||
this.dtstart()
|
||||
this.dtend()
|
||||
this.summary()
|
||||
this.description()
|
||||
this.location()
|
||||
this.end()
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) begin() {
|
||||
this.buffer.Write("BEGIN:VEVENT\n")
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) end() {
|
||||
this.buffer.Write("END:VEVENT\n")
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) uid() {
|
||||
this.serializeStringProp("UID", this.event.Id)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) summary() {
|
||||
this.serializeStringProp("SUMMARY", this.event.Summary)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) description() {
|
||||
this.serializeStringProp("DESCRIPTION", this.event.Description)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) location() {
|
||||
this.serializeStringProp("LOCATION", this.event.Location)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) dtstart() {
|
||||
this.serializeTimeProp("DTSTART", this.event.StartAtUTC())
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) dtend() {
|
||||
this.serializeTimeProp("DTEND", this.event.EndAtUTC())
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) created() {
|
||||
this.serializeTimeProp("CREATED", this.event.CreatedAtUTC)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) lastModified() {
|
||||
this.serializeTimeProp("LAST-MODIFIED", this.event.ModifiedAtUTC)
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) serializeStringProp(name, value string) {
|
||||
if value != "" {
|
||||
escapedValue := escapeTextType(value)
|
||||
this.buffer.Write("%s:%s\n", name, escapedValue)
|
||||
}
|
||||
}
|
||||
|
||||
func (this *calEventSerializer) serializeTimeProp(name string, value *time.Time) {
|
||||
if value != nil {
|
||||
this.buffer.Write("%s:%s\n", name, value.Format(eventSerializerTimeFormat))
|
||||
}
|
||||
}
|
94
vendor/github.com/laurent22/ical-go/ical/todo.go
generated
vendored
Normal file
94
vendor/github.com/laurent22/ical-go/ical/todo.go
generated
vendored
Normal file
@ -0,0 +1,94 @@
|
||||
package ical
|
||||
|
||||
// import (
|
||||
// "time"
|
||||
// "strconv"
|
||||
// "strings"
|
||||
// )
|
||||
//
|
||||
// func TodoFromNode(node *Node) Todo {
|
||||
// if node.Name != "VTODO" { panic("Node is not a VTODO") }
|
||||
//
|
||||
// var todo Todo
|
||||
// todo.SetId(node.PropString("UID", ""))
|
||||
// todo.SetSummary(node.PropString("SUMMARY", ""))
|
||||
// todo.SetDescription(node.PropString("DESCRIPTION", ""))
|
||||
// todo.SetDueDate(node.PropDate("DUE", time.Time{}))
|
||||
// //todo.SetAlarmDate(this.TimestampBytesToTime(reminderDate))
|
||||
// todo.SetCreatedDate(node.PropDate("CREATED", time.Time{}))
|
||||
// todo.SetModifiedDate(node.PropDate("DTSTAMP", time.Time{}))
|
||||
// todo.SetPriority(node.PropInt("PRIORITY", 0))
|
||||
// todo.SetPercentComplete(node.PropInt("PERCENT-COMPLETE", 0))
|
||||
// return todo
|
||||
// }
|
||||
//
|
||||
// type Todo struct {
|
||||
// CalendarItem
|
||||
// dueDate time.Time
|
||||
// }
|
||||
//
|
||||
// func (this *Todo) SetDueDate(v time.Time) { this.dueDate = v }
|
||||
// func (this *Todo) DueDate() time.Time { return this.dueDate }
|
||||
//
|
||||
// func (this *Todo) ICalString(target string) string {
|
||||
// s := "BEGIN:VTODO\n"
|
||||
//
|
||||
// if target == "macTodo" {
|
||||
// status := "NEEDS-ACTION"
|
||||
// if this.PercentComplete() == 100 {
|
||||
// status = "COMPLETED"
|
||||
// }
|
||||
// s += "STATUS:" + status + "\n"
|
||||
// }
|
||||
//
|
||||
// s += encodeDateProperty("CREATED", this.CreatedDate()) + "\n"
|
||||
// s += "UID:" + this.Id() + "\n"
|
||||
// s += "SUMMARY:" + escapeTextType(this.Summary()) + "\n"
|
||||
// if this.PercentComplete() == 100 && !this.CompletedDate().IsZero() {
|
||||
// s += encodeDateProperty("COMPLETED", this.CompletedDate()) + "\n"
|
||||
// }
|
||||
// s += encodeDateProperty("DTSTAMP", this.ModifiedDate()) + "\n"
|
||||
// if this.Priority() != 0 {
|
||||
// s += "PRIORITY:" + strconv.Itoa(this.Priority()) + "\n"
|
||||
// }
|
||||
// if this.PercentComplete() != 0 {
|
||||
// s += "PERCENT-COMPLETE:" + strconv.Itoa(this.PercentComplete()) + "\n"
|
||||
// }
|
||||
// if target == "macTodo" {
|
||||
// s += "SEQUENCE:" + strconv.Itoa(this.Sequence()) + "\n"
|
||||
// }
|
||||
// if this.Description() != "" {
|
||||
// s += "DESCRIPTION:" + encodeTextType(this.Description()) + "\n"
|
||||
// }
|
||||
//
|
||||
// s += "END:VTODO\n"
|
||||
//
|
||||
// return s
|
||||
// }
|
||||
//
|
||||
// func encodeDateProperty(name string, t time.Time) string {
|
||||
// var output string
|
||||
// zone, _ := t.Zone()
|
||||
// if zone != "UTC" && zone != "" {
|
||||
// output = ";TZID=" + zone + ":" + t.Format("20060102T150405")
|
||||
// } else {
|
||||
// output = ":" + t.Format("20060102T150405") + "Z"
|
||||
// }
|
||||
// return name + output
|
||||
// }
|
||||
//
|
||||
//
|
||||
// func encodeTextType(s string) string {
|
||||
// output := ""
|
||||
// s = escapeTextType(s)
|
||||
// lineLength := 0
|
||||
// for _, c := range s {
|
||||
// if lineLength + len(string(c)) > 75 {
|
||||
// output += "\n "
|
||||
// lineLength = 1
|
||||
// }
|
||||
// output += string(c)
|
||||
// lineLength += len(string(c))
|
||||
// }
|
||||
// return output
|
||||
// }
|
Reference in New Issue
Block a user