Cyrus CalDAV Scheduling Flowchart
  caldav_put() - create/modify via HTTP PUT on a
    resource or POST (add-member) on a calendar
  
    - Check if the new resource is a scheduling resource (contains
      ORGANIZER property).  If not, skip to step 4.
- Check for (and load) any existing resource.
- Check if the authenticated user matches ORGANIZER:
      
    
- Store the new/modified resource.
caldav_delete_sched() - remove via HTTP
    DELETE on a resource
  
    - Check if the existing resource is a scheduling resource (has
      Schedule-Tag).  If not, we are done.
- Load the existing resource.
- Check if the authenticated user matches ORGANIZER.  If yes,
      goto sched_request(), otherwise
      goto sched_reply().
caldav_post() - busytime query via HTTP POST on
    Scheduling Outbox
  
    - Check the ACL on the owner's Scheduling Outbox.  If the
      authenticated user doesn't have the DACL_SCHEDFB right, fail.
- sched_busytime_query().
  sched_pollstatus - perform a voter
    response update
  
    
  
  sched_request() - perform an organizer
  request / attendee status update
  
    - Check the ACL on the owner's Scheduling Outbox.  If the
      authenticated user doesn't have the DACL_INVITE right, fail.
- If the request includes a resource, then set METHOD:REQUEST,
      otherwise set METHOD:CANCEL.
- Create an iTIP message template, copying over any
      CALSCALE property and VTIMEZONE components.
- If not an attendee status update and the existing resource is a
      scheduling resource:
      Foreach component in the existing resource, add it and
      its SEQUENCE to our hash table keyed by RECURRENCE-ID (for
      comparison against new/modified resource).
- Create a hash table of attendees.  This will hold
      attendee-specific iTIP messages.
- Foreach component in the new/modified resource:
      - Lookup (by RECURRENCE-ID) and remove the component from the
        hash table of existing components.
- If the component exists compare all of DTSTART, DTEND,
        DURATION, RRULE, RDATE, EXDATE to those of the new
        component.
- If the component is new or changed,
        then process_attendees().
- Foreach remaining component in the hash table of existing
      components do sched_cancel().
- Foreach iTIP message in our hash table of
      ATTENDEES, sched_deliver() the iTIP
      message.
- Foreach component in the new/modified resource update the
      SCHEDULE-STATUS of each ATTENDEE.
process_attendees() - create a suitable
    iTIP request message for each attendee
  
    - Foreach ATTENDEE in the component, remove the SCHEDULE-STATUS
      parameter, and set PROPSTAT=NEEDS-ACTION if required.
- Make a copy of the component and
      clean_component().
- Foreach ATTENDEE in the cleaned component:
      - Check the CalDAV Scheduling parameters.  If SCHEDULE-AGENT
        != SERVER, skip to the next attendee.
- Lookup attendee in our hash table.
- If it doesn't exist, create a clone of our iTIP template and
        insert it into our hash table of attendees.
- Add the component to the attendee's iTIP message.
- Add the component “number” to our mask of new components
        appearing in the attendee's iTIP message.
- If the component is not the "master", foreach attendee do
      sched_exclude().
sched_exclude() - exclude an attendee from a
    recurrence instance
  
    - If the component did not appear in the attendee's iTIP
      message, add an EXDATE property (based on the RECURRENCE-ID of
      the component) to the master component of the attendee's iTIP
      message.
sched_cancel() - cancel an organizer event/task
  
    - Set STATUS:CANCELLED on the component.
- process_attendees().
  sched_reply() - perform an attendee reply
  
    - Check the CalDAV Scheduling parameters on ORGANIZER.  If
      SCHEDULE-AGENT != SERVER, we are done.
- Check the ACL on the owner's Scheduling Outbox.  If the
      authenticated user doesn't have the DACL_REPLY right, fail.
- Create a new iTIP (METHOD:REPLY) message, copying over any
      CALSCALE property and VTIMEZONE components.
- Foreach component in the existing resource:
      - trim_attendees().
- Add the trimmed component and the attendee's PARTSTAT to our
        hash table keyed by RECURRENCE-ID (for comparison against
        new/modified resource).
- Foreach component in the new/modified resource:
      - trim_attendees().
- Lookup (by RECURRENCE-ID) and remove the component from the
        hash table of existing components.
- If the component exists:
        - If component is VPOLL, add voter responses to REPLY
          via sched_vpoll_reply().
- Otherwise, compare the PARTSTAT of the ATTENDEE to that of
        the new component.
- If the component is new or the PARTSTAT has changed:
        - clean_component().
- Add the component to our iTIP message.
- Add the component “number” to our mask of new components
          appearing in our iTIP message.
- Foreach remaining component in the hash table of existing
      components do sched_decline().
- sched_deliver() our iTIP
      message.
- Foreach component in the new/modified resource that appeared
      in our iTIP message, update the SCHEDULE-STATUS of the ORGANIZER.
trim_attendees() - remove all attendees
    other than the one replying
  
    - Clone the component and remove all ATTENDEE properties other
      than the one corresponding to the owner of the calendar.
- Return the ATTENDEE property of owner, his/her PARTSTAT
      parameter, and the RECURRENCE-ID of the component.
sched_vpoll_reply() - add voter
    responses to VPOLL reply
  
    
  
sched_decline() - decline a recurrence
    instance for an attendee
  
    - Set PARTSTAT of ATTENDEE to DECLINED.
- clean_component().
- Add the component to our iTIP message.
  clean_component() - sanitize a component
    for use in an iTIP message
  
    - Update DTSTAMP.
- Remove any VALARM components.
- For a reply/decline only, remove scheduling parameters from
      ORGANIZER.
sched_deliver() - deliver an iTIP message to
    a recipient
  
    - Lookup the recipient.
- If local to our server goto
      sched_deliver_local(),
      otherwise goto
      sched_deliver_remote().
  sched_deliver_local() - deliver an
    iTIP message to a local user
  
    - Check the ACL on the owner's Scheduling Inbox.  If the
      sender doesn't have the proper right (DACL_INVITE for
      request/cancel, DACL_REPLY for reply), fail.
- Search the recipient's calendars for a resource having the
      specified UID.
- If the resource doesn't exist:
      - If the iTIP method is REPLY, fail (we are done).
- If the iTIP method is CANCEL, ignore it (we are done).
- Otherwise, create a new (empty) attendee object and target
        the recipient's Default calendar.
- Otherwise, load the existing resource.
- Update the new/existing resource:
      - If the iTIP method is CANCEL, set STATUS:CANCELLED on all
        existing components.
- If the iTIP method is REPLY, do
        deliver_merge_reply().
- If the iTIP method is REQUEST, do
        deliver_merge_request().
- If the iTIP method is POLLSTATUS, do
        deliver_merge_pollstatus().
- Store the new/updated resource in the recipient's target
      calendar.
- Record the delivery status (SCHEDULE-STATUS).
- If the iTIP message is something other than just a PARTSTAT
      update from an attendee, store the iTIP message as a new
      resource in the recipient's Inbox.
- If the iTIP method is REPLY, send an update other attendees
      via sched_pollstatus() (VPOLL only)
      or sched_request().
deliver_merge_reply() - update
    an organizer resource with an attendee reply
  
    - Foreach component in the existing resource, add it to our
        hash table keyed by RECURRENCE-ID (for comparison against
        iTIP message).
- Foreach component in the iTIP message:
      - Lookup (by RECURRENCE-ID) the component from the
        hash table of existing components.
- If the component doesn't exist (new recurrence overridden by
        ATTENDEE) create a new recurring component:
        - Clone the existing master component.
- Remove the RRULE property.
- Add the RECURRENCE-ID from the iTIP message.
- Replace the DTSTART, DTEND, SEQUENCE properties with those
          from the iTIP message.
- Add the new component to our existing resource.
- Get the sending ATTENDEE from the iTIP message.
- Find the matching ATTENDEE in the existing component.
- If not found (ATTENDEE added themselves to this recurrence),
        add new ATTENDEE to the component.
- Set the ATTENDEE PARTSTAT, RSVP, and SCHEDULE-STATUS
        parameters in the existing component.
- If the component is VPOLL, update the voter responses in the
        existing component via
        deliver_merge_vpoll_reply().
- Return the sending ATTENDEE.
deliver_merge_vpoll_reply() - update
    an organizer resource with voter responses
  
    - Foreach sub-component in the existing resource, replace any
        voter response(s) with those from the reply.
deliver_merge_request() -
    create/update an attendee resource with an organizer request
  
    - Foreach VTIMEZONE component in the existing resource, add it
        to our hash table keyed by TZID (for comparison against iTIP
        message).
- Foreach VTIMEZONE component in the iTIP message:
      - Lookup (by TZID) the VTIMEZONE component from the hash table of
        existing components.
- If the component exists, remove it from the existing
        object.
- Add the VTIMEZONE from the iTIP message to our existing
        object.
- Foreach component in the existing resource, add it to our
        hash table keyed by RECURRENCE-ID (for comparison against
        iTIP message).
- Foreach component in the iTIP message:
      - Clone a new component from the iTIP component.
- Lookup (by RECURRENCE-ID) the component from the
        hash table of existing components.
- If the component exists:
        - Compare the SEQUENCE of the new component to the existing
          component to see if it has changed.
- Copy any COMPLETED, PERCENT-COMPLETE, or TRANSP properties
          from the existing component to the new component.
- Copy any ORGANIZER SCHEDULE-STATUS parameter
          from the existing component to the new component.
- Remove the existing component from the existing object.
- Add the new component to the existing object.
deliver_merge_pollstatus() -
    update voter responses on a voter resource
  
    - Foreach sub-component in the existing resource, add it to our
        hash table keyed by POLL-ITEM-ID (for comparison against
        iTIP message).  The sub-component entry includes a hash table
        of VOTERs.
- Foreach sub-component in the iTIP message:
      - Lookup (by POLL-ITEM-ID) the sub-component from the
        hash table of existing sub-components.
- If the component exists, foreach VOTER in the sub-component
        in the iTIP message:
        
          - Lookup VOTER in the hash table of existing
            sub-component.
- Add/update VOTER response.
 
  sched_deliver_remote() - deliver an
    iTIP message to a remote user
  
    - If the recipient is local to our Murder, goto
      isched_send(), otherwise
      goto imip_send().
- Retrieve status of iTIP message delivery.
isched_send() - deliver an iTIP message to a
    remote user via iSchedule (HTTP)
  
  
imip_send() - deliver an iTIP message to a
    remote user via iMIP (SMTP)
  
  
  sched_busytime_query() - perform a
    busytime query
  
  
busytime_query_local() - perform a
    busytime query on a local user
  
  
busytime_query_remote() - perform a
  busytime query on a remote user