summaryrefslogtreecommitdiff
path: root/pim/lib/cal/tree.go
diff options
context:
space:
mode:
Diffstat (limited to 'pim/lib/cal/tree.go')
-rw-r--r--pim/lib/cal/tree.go58
1 files changed, 58 insertions, 0 deletions
diff --git a/pim/lib/cal/tree.go b/pim/lib/cal/tree.go
index cd9e9d2..f78f2eb 100644
--- a/pim/lib/cal/tree.go
+++ b/pim/lib/cal/tree.go
@@ -112,6 +112,7 @@ func (s *Server) buildEvents(root *fs.StaticDir, evs []*Event) {
s.file(ed, "attendees", attendeeText(e.Attendees))
}
s.file(ed, "uid", e.UID+"\n")
+ s.addPartstat(ed, e)
s.file(ed, "raw", e.Raw)
}
}
@@ -215,3 +216,60 @@ func attendeeText(as []Attendee) string {
}
return b.String()
}
+
+// addPartstat publishes your own reply status for an event.
+//
+// Reading it says where you stand. Writing it answers the invitation --
+// but only if the backend can actually do both halves of that: update
+// the calendar and tell the organiser. A published .ics can do neither,
+// so there the file is read-only and says why.
+func (s *Server) addPartstat(dir *fs.StaticDir, e *Event) {
+ var me []string
+ if id, ok := s.be.(Identity); ok {
+ me = id.Me()
+ }
+ cur := "" // no identity, or not an attendee
+ for _, a := range e.Attendees {
+ for _, m := range me {
+ if strings.EqualFold(a.Email, m) || strings.EqualFold(a.Name, m) {
+ cur = a.Partstat
+ }
+ }
+ }
+ if cur == "" && len(me) == 0 {
+ cur = "unknown"
+ } else if cur == "" {
+ cur = "not-an-attendee"
+ }
+
+ rsvp, canWrite := s.be.(RSVPer)
+ mode := uint32(0444)
+ if canWrite {
+ mode = 0666
+ }
+ st := s.fsys.NewStat("partstat", s.user, s.user, mode)
+ base := fs.NewStaticFile(st, []byte(cur+"\n"))
+ if !canWrite {
+ dir.AddChild(base)
+ return
+ }
+ uid := e.UID
+ dir.AddChild(&fs.WrappedFile{
+ File: base,
+ WriteF: func(fid uint64, off uint64, data []byte) (uint32, error) {
+ want := strings.ToUpper(strings.TrimSpace(string(data)))
+ switch want {
+ case "ACCEPTED", "DECLINED", "TENTATIVE":
+ default:
+ return 0, fmt.Errorf("partstat: want ACCEPTED, DECLINED or TENTATIVE")
+ }
+ if len(me) == 0 {
+ return 0, fmt.Errorf("partstat: no me= for this calendar")
+ }
+ if err := rsvp.RSVP(uid, time.Time{}, want); err != nil {
+ return 0, err
+ }
+ return uint32(len(data)), nil
+ },
+ })
+}