////////////////////////////////////////////////////////////////////// // DATETIME Elements are specified as follows: // // // means: // // // // YYYY - 4 digit year // MM - 2 digit month // DD - 2 digit day // Optionally: // 'T' the literal char "T" then // HH - 2 digit hour (00-23) // MM - 2 digit minute (00-59) // SS - 2 digit second (00-59) // ...and finally an optional "Z" meaning that the time is UTC, // otherwise the tz="TIMEZONE" param MUST be specified with the DATETIME // // e.g: // 20050612 June 12, 2005 // 20050315T18302305Z March 15, 2005 6:30:23.05 PM UTC // // // tz="java timezone identifier" (see list at bottom of this file!) // // // NOTE: if DATETIME has a time component (after the 'T') then it must either be specified // in UTC (has a trailing 'Z') or else the tz= MUST be specified!!! // ////////////////////////////////////////////////////////////////////// // "DURATION" element (specified-duration) // // // // Special note: if WEEKS are specified, NO OTHER OFFSET MAY BE // SPECIFIED (weeks must be alone, per RFC2445) // ////////////////////////////////////////////////////////////////////// // // CREATEAPPOINTMENT // ----------------- // This is the API to create a new Appointment, optionally sending out // meeting Invitations to other people. // // See soap.txt for complete list of attrs on [*] // users to send request to {subject} [ // Time zone components define the time zones referenced in the // rest of the , for start/end times and recurrence rules. // Time zones defined in the LDAP server don't need to be defined // here, but all custom time zones must be defined before being // referenced. stdoff="" // offset from UTC in standard time; local = UTC + offset [dayoff=""] // offset from UTC in daylight time; present only if DST is used [stdname="abbrev"] // abbreviated name for standard time (e.g. PST) [dayname="abbrev"] // abbreviated name for daylight time (e.g. PDT) > [ // If daylight savings time is not used, and must be // omitted. If DST is used, both and must be present. ]? ]* // always present in responses; optional in requests if all TZs used // in are defined in LDAP [ // Start date-time (required) ( // End date-time OR // ) // one is required [DESCRIPTION] // present if noBlob=1 and invite has plain text description [HTML description] // present if noBlob=1 and invite has html description // organizer ]* // non-standard parameters /> // attendee(s) ]* // non-standard parameters />* // role = CHAir, REQuired, OPTional, NON-participant (ie informational) // ptst (ParTSTat - participation status) = "NE"eds-action, "TE"ntative, "AC"cept, "DE"clined, // "DG" (delegated), "CO"mpleted (todo), "IN"-process (todo), // "WA"iting (custom value only for todo), "DF" (deferred; custom value only for todo) // cutype (calendar user type) = INDividual, GROup, RESource, ROOm, UNKnown // sentBy, member, delTo and delFrom values are email addresses // CATEGORIES [CATEGORY]* // COMMENTs [COMMENT]* // CONTACTs [CONTACT]* // GEO [ // see below [ [ // count of instances to generate ]? // optional UNTIL or COUNT param [ // each of the following can occur at most once // COUNT: positive integer // second: 0 to 59 // minute: 0 to 59 // hour: 0 to 23 // num: 1 to 53, WEEKDAY: SU,MO,TU,WE,TH,FR,SA [...] // e.g. // // // // // // // means every Monday of the year, // plus Tuesday of 3rd week of the year, // plus Wednesday of 4th week, // plus Sunday of last week of the year. // num: 1 to 31 // e.g. // means first day of the month, plus the 2nd day of the month, // plus the 7th from last day of the month. // num: 1 to 366 // e.g. // means January 1st, January 2nd, and December 31st. // num: 1 to 53 // e.g. // means first week, 2nd week, and last week of the year. // month: 1 to 12 // poslist: 1 to 366 // MUST only be used in conjunction with another // element. // WEEKDAY: SU,MO,TU,WE,TH,FR,SA ]* [ // custom fields ]* ]* // RDATE/EXDATE [ // Start DATE-TIME only. Instance has same duration as // the default duration. // RDATE[;VALUE=DATE-TIME][;TZID=...]:val,val,val,... * ]* [ // Start DATE only. (all-day) Instance has same // duration as the default duration. // RDATE;VALUE=DATE[;TZID=...]:val,val,val,... * ]* [ // Start date/time and either end date/time or duration. // Only DATE-TIME format is allowed for start/end. // This format is allowed in only, but not in // . // RDATE;VALUE=PERIOD[;TZID=...]:val,val,val,... OR * ]* //////////// // // These next two ( and ) are only valid for APIs that deal with the // entire appointment such as SetAppointment and GetAppointment: they are IGNORED by // other APIs. [ ...SAME AS ABOVE... ]* [ ]* ]* ] // VALARMs (RFC2445 Section 4.6.6) [ // has the same attributes as and an optional // "related" attribute. Default value of "related" is "START". OR {reminder text to display} // has the same attributes as and an additional // required count attribute. The duration is how often to repeat // the alarm, and the count is how many times to trigger the // alarm IN ADDITION TO the initial alarm. [] [ []* ]* ]* [ // same as in DISPLAY alarm [] // same as in DISPLAY alarm [ OR {base64-encoded binary data} ] [ []* ]* ]* [ // same as in DISPLAY alarm [] // same as in DISPLAY alarm {email body} {email subject} + // attendees (one or more email recipients) [] // sam as in AUDIO alarm [ []* ]* ]* [ // same as in DISPLAY alarm [] // same as in DISPLAY alarm [{description test}] // same as in AUDIO alarm [ []* ]* ]* [ // non-standard properties (see RFC2445 section 4.8.8.1) // e.g. // iCalendar: // // X-FOO-HELLO;X-FOO-WORLD=world:hello // // SOAP: // // // // []* ]* ]+ ... [...] // CREATING EXCEPTIONS // -------------------- // // Just like CreateAppointment, except that instead of specifying a target FOLDER, you // specify an existing PID and component-number // // *** If id= and comp= are set, then you are creating an EXCEPTION: // // The default invite for the appointment MUST have a recurrence rule.... // otherwise you should just be using ModifyAppointment! // // EXCEPTION_ID is start-time for the particular instance you are overriding // EXCEPTION_ID MUST be some instance that matches the default-invite for // the appointment...that is, it MUST NOT BE the start time of the exception! // // **The above is important** So let me explain: // // Given an appointment, happens every Wednesday @ 9am, with an exception that moves // it to 10am on 6/22 // // So our instances for June, 2005 are: // 6/1 9am // 6/8 9am // 6/15 9am // 6/22 10am (an existing EXCEPTION) // 6/29 9am // // So, if a user clicks on the 6/22 10am appointment and wants to modify it, you MUST NOT // try to use to modify it! // You MUST use ModifyAppointmentRequest instead!!! // // On the other hand, if a user clicks on the 6/15 9am appointment and wants to modify it, // then this is the right API to use. // ... // optional: client can request the UID to use [ // EXCEPTION_ID -- this is the DateTime of some instance in the default appointment ... ---------------------- RECURRENCE SPECIFIERS! holds a recurence. Note that the initial instance is NOT specified by , it is implied by the s= and (e=|dur=) parameters. The initial instance may NOT be modified by an parameter inside a - dates or rules which ADD instances. ADDs are evaluated before EXCLUDEs - dates or rules which EXCLUDE instances. - an SDATE that is added or excluded. D+DATE values may ONLY be used if this is an all-day-event! - a RULE for specifying added/exluded instances - "SEC","MIN","HOU","DAI","WEE","MON","YEA" - event happens every INTERVAL Seconds/Minutes/Hours/etc. - Default is 1. Number of (secs/mins/hours/etc) between occurences. - INCLUSIVE date to stop (ie if there is a recurrence on that date, it IS added to list - number of occurences to be generated by this rule ,.... --- TODO ---------------------- [] // if echo="1" was specified in the request ////////////////////////////////////////////////////////////////////// // // Directly set status of an entire appointment. This API is intended // for mailbox Migration (ie migrating a mailbox onto this server) and // is not used by normal mail clients. // // need to specifiy folder for appointment // // need way to add message WITHOUT processing it for calendar parts. // need to generate and patch-in the iCalendar for the but w/o // actually processing the as a new request // // TODO: need way to link message to appointment after the fact // // ...see CreateAppointmentRequest... ************** ONE OF: 1) XML format from CreateAppointment ... // EXCEPTION_ID -- this is the DateTime of some instance in the default appointment ... meeting notes parts OR 2) Raw MimeMessage import: RAW RFC822 MESSAGE (XML-encoded) ***MUST CONTAIN A text/calendar PART*** [ ... ... [ Same as under above ... **1 of the 3 forms above** ]* [ Same as under above ... **1 of the 3 forms above** If using form, these elements and attributes must be present: [...]* // RECURRENCE-ID // DTSTART // ORGANIZER + // ATTENDEEs meeting notes parts All others are ignored. ]* // List of replies received from attendees. If SetAppointmenRequest doesn't contain // block, existing replies will be kept. If element is provided with no // elements inside, existing replies will be removed, replaced with an empty set. If // contains one or more elements, existing replies are replaced with the ones provided. [ []* ] // one or more replies which we have sent out ////////////////////////////////////////////////////////////////////// // // GetApptSummaries // // DEPRECATED -- this API is deprectated. Use with // the calExpandInstStart and calExpandInstEnd parameters // // // // -- folder-id: optional folder to constrain requests to; otherwise, searches all folders but trash and spam // // // ( // FRAGMENT // [CATEGORY]* // [ // [FRAGMENT IF DIFFERENT FROM DEFAULT] // [] // duration this instance if not same as default // [] // isOrg flag if not same as default // [] // otherAtt flag if not same as default // [CATEGORY]* // [ // )+ // // )* // // // -- fba: actual free-busy status: Free, Busy, busy-Tentative, busy-Unavailable (a.k.a. OutOfOffice) // While free-busy status is simply a property of an event that // is set during creation/update, "actual" free-busy status is the true // free-busy state that depends on appt/invite free-busy, event scheduling // status (confirmed vs. tentative vs. cancel), and more importantly, the // attendee's participation status. For example, actual free-busy is // busy-Tentative for an event with Busy free-busy value until the attendee // has acted on the invite. // -- transp: transparency: Opaque, Transparent // -- status: status of event: TENTative, CONFirmed or CANCelled // -- ptst: **your** participation status: NEeds-action, TEntative, ACcept, DEclined, DG (delegated) // -- get the summary of appointments for a specified time period... // -- otherAtt: 1 if there are other attendees to the meeting // -- alarm: 1 if there are some alarms // -- recur: 1 if this is a recurring appointment // -- id is mail_item id of APPOINTMENT object // -- invId is mail_item id of invite message with detailed information // -- comp is component number (invite # within the message) // -- parameters in the apptSum are "defaults" -- are same in instance unless specified // // END DEPRECATED BLOCK // ////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////// // // GetFreeBusyRequest // // (f)ree (b)usy busy-(t)entative busy-(u)navailable or (n)o-data elements // format below. [excludeUid="UID of appointment to exclude from free/busy search"] > []* // id is always account email; it is not zimbraId as the attribute name may suggest * * * * // a.k.a. out of office * // could not retrieve data for that user ////////////////////////////////////////////////////////////////////// // // GetWorkingHoursRequest // // (f)ree busy-(u)navailable or (n)o-data elements // // User's working hours within the given time range are expressed in a similar format as GetFreeBusy. // Working hours are indicated as free, non-working hours as unavailable/out of office. The entire time // range is marked as unknown if there was an error determining the working hours, e.g. unknown user. // // id is always account email; it is not zimbraId as the attribute name may suggest * // working hours * // non-working hours * // could not retrieve data for that user ////////////////////////////////////////////////////////////////////// // // CancelAppointment(DEFAULT-INVITE-ID, COMPONENT-NUMBER) // // NOTE: If canceling an exception, the original instance (ie the one the exception was "excepting") WILL NOT // be restored when you cancel this exception. // // if is set, then this cancels just the specified instance or range of instances, // otherwise it cancels the entire appointment. If is not set, then id MUST refer to the default // invite for the appointment. // [] // definition for TZID referenced by DATETIME in []? [ [*] // users to send update to [{subject of cancellation mail}] ... ] ////////////////////////////////////////////////////////////////////// // // GetMsgResponse // // CALENDAR PART for Invite messages // // mesage with invite [*] // optional users to send request to [ // Start date-time (required) ( // End date-time OR ) [] // RECURRENCE-ID, if this is an exception // organizer * // attendee [ RECURRENCE PART ] // CATEGORIES [CATEGORY]* // COMMENTs [COMMENT]* // CONTACTs [CONTACT]* // GEO [ ]+ [...] ////////////////////////////////////////////////////////////////////// // // ModifyAppointment // // Modify an appointment, or if the appointment is a recurrence then modify the "default" // invites: that is, all instances that do not have exceptions // // If the appointment has a , then the following caveats are worth mentioning: // // -- If any of: START, DURATION, END or RECUR change, then all exceptions are implicitly canceled! // rest is SAME as CreateAppointmentRequest [] // if echo="1" was specified in the request "ms" and "rev" are used for conflict detection. By setting these, the request indicates which version of the appointment it is attempting to modify. If the appointment was updated on the server between the fetch and modify, an INVITE_OUT_OF_DATE exception will be thrown. ////////////////////////////////////////////////////////////////////// // // CreateAppointmentException (invId, compNum, instanceid) // // The default invite for the appointment MUST have a recurrence rule.... // otherwise you should just be using ModifyAppointment! // // EXCEPTION_ID is start-time for the particular instance you are overriding // EXCEPTION_ID MUST be some instance that matches the default-invite for // the appointment...that is, it MUST NOT BE the start time of the exception! // // **The above is important** So let me explain: // // Given an appointment, happens every Wednesday @ 9am, with an exception that moves // it to 10am on 6/22 // // So our instances for June, 2005 are: // 6/1 9am // 6/8 9am // 6/15 9am // 6/22 10am (an existing EXCEPTION) // 6/29 9am // // So, if a user clicks on the 6/22 10am appointment and wants to modify it, you MUST NOT // try to use to modify it! // You MUST use ModifyAppointmentRequest instead!!! // // On the other hand, if a user clicks on the 6/15 9am appointment and wants to modify it, // then this is the right API to use. // Just like CreateAppointmentRequest above: ... ... // EXCEPTION_ID -- this is the DateTime of some instance in the default appointment ... ... "ms" and "rev" are used for conflict detection. The create exception request indicates the baseline series version. If the appointment was modified on the server between the series fetch and exception creation, an INVITE_OUT_OF_DATE exception will be thrown. ////////////////////////////////////////////////////////////////////// // // ExpandRecur // * [ // series // DTSTART [ OR ] // DTEND/DURATION // RRULE/RDATE/EXDATE ] [ // modified instances // DTSTART [ OR ] // DTEND/DURATION // RECURRENCE-ID ]* [ // canceled instances // RECURRENCE-ID ]* * ////////////////////////////////////////////////////////////////////// // // GetRecur // // Retrieve the recurrence definition of an appointment // [same as ExpandRecurRequest content] ////////////////////////////////////////////////////////////////////// // // CheckRecurConflicts // // Check conflicts in recurrence against list of users. // Use all=1 to get all instances, even those without conflicts. By default // only instances that have conflicts are returned. // tz/comp/except/cancel elements (same as ExpandRecurRequest content) []* * // for all users who have a conflict with the instance // fb is free/busy status (Busy, Tentative or Out-of-office) * ////////////////////////////////////////////////////////////////////// // // Retrieve the unparsed (but XML-encoded (") iCalendar data for an Invite // // This is intended for interfacing with 3rd party programs // -- if id is specified, gets iCalendar representation for one Invite -- if id is not specified, then start/end MUST be, Calendar data is returned for entire specified range ICALENDAR DATA ////////////////////////////////////////////////////////////////////// // // SendInviteReply // // [] // definition for TZID referenced by DATETIME in []? // reply to just one instance of the specified Invite (default is all instances) [...] id : unique ID of the invite (and component therein) you are replying to comp : component number of the invite verb : ACCEPT, COMPLETED, DECLINE, DELEGATED, TENTATIVE (Completed/Delegated are NOT supported as of 9/12/2005) m : embedded message, if the user wants to send a custom update message. The client is responsible for setting the message recipient list in this case (which should include Organizer, if the client wants to tell the organizer about this response) updateOrganizer: true by default. if false then only make the update locally. Note that earlier documentation implied incorrectly that if this was false it would be ignored and treated as being true if an element is present. Also take a note that, if RSVP setting in original invite is not present or FALSE then updateOrganizer will be treated as FALSE. status: OK, OLD (a newer invite exists for that appointment), ALREADY-REPLIED, FAIL (other failure) ////////////////////////////////////////////////////////////////////// // // CounterAppointment // // Propose a new time/location. Sent by meeting attendee to organizer. // The syntax is very similar to CreateAppointmentRequest. // // // From // To ... // Subject ... // plain/html texts // iCalendar COUNTER object [] [] // RECURRENCE-ID if dealing with an instance of a recurrence // start time // end time // organizer (the appointment's organizer who will be receiving the counter proposal) // attendee (the attendee sending this counter request) "ms" and "rev" are used for conflict detection. By setting these, the request indicates which version of the appointment it is attempting to propose. If the appointment was updated on the server between the fetch and modify, an INVITE_OUT_OF_DATE exception will be thrown. ////////////////////////////////////////////////////////////////////// // // DeclineCounterAppointment // // Decline a change proposal from an attendee. Sent by organizer to an attendee // who has previously sent a COUNTER message. The syntax is very similar to // CreateAppointmentRequest. // // From // To ... // Subject ... // plain/html texts // iCalendar DECLINECOUNTER object [] // comp should echo the same data [] // RECURRENCE-ID if dealing with an instance of a recurrence // start time // end time // organizer // attendee (the attendee whose COUNTER proposal is being declined) ... // same as sending email; specify subject, recipient, etc. // equivalent of iCalendar DECLINECOUNTER VEVENT ////////////////////////////////////////////////////////////////////// // // ForwardAppointment // // Used by an attendee to forward an instance or entire appointment to // another user who is not already an attendee. // // [] // definition for TZID referenced by DATETIME in [] // RECURRENCE-ID if forwarding a single instance of a recurring appointment // From + // To ... // Subject ... // plain/html texts ////////////////////////////////////////////////////////////////////// // // ForwardAppointmentInvite // // Used by an attendee to forward an appointment invite email to // another user who is not already an attendee. // // To forward an appointment item, use ForwardAppointmentRequest instead. // // // From + // To ... // Subject ... // plain/html texts ////////////////////////////////////////////////////////////////////// // // GetAppointment // // Returns the metadata info for each Invite that makes up this appointment. // // The content (original email) for each invite is stored within the Appointment itself in // a big multipart/digest containing each invite in the appointment as a sub-mimepart -- // it can be retreived from the content servlet: // // http://servername/service/content/get?id= // // // The content for a single Invite can be requested from the content servlet (or from ) // Individual The client can ALSO request just the content for each individual invite using a // compound item-id request: // http://servername/service/content/get?id="calItemId-invite_mail_item_id" // [ ... ---See part of GetMsgResponse--- ... [ ... ] // body content, if includeContent="1" was specified in the request ]+ // List of replies received from attendees. [ []* ] // one or more replies which we have sent out if sync="1" present, return modified date (md) on appointment. ------------------------------------------------------------------------------- {content-type} = only currently supported content type is "text/calendar" (and its nickname "ics") {folder-id} = optional folder id to import appointments into {attach-upload-id} = attachment id from upload server. If specified, then body of content is ignored. ------------------------------------------------------------------------------- + // so the client knows when to trigger the next alarm // See definition under SearchResponse, in soap.txt + // one for each in the request ------------------------------------------------------------------------------- + // so the client knows when to trigger the next alarm // See definition under SearchResponse, in soap.txt + // one for each in the request ------------------------------------------------------------------------------- + // local and/or remote calendar folders [ // optional timezone specifier // References an existing server-known timezone by ID OR ... // full specification of a custom timezone; see CreateAppointmentRequest ] [yyyymmdd]* // e.g. 20080221 [error message from the exception (but no stack trace)]* Date is returned if there is at least one appointment on that date. The date computation uses the requesting (authenticated) account's time zone, not the time zone of the account that owns the calendar folder. ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- ------------------------------------------------------------------------------- NOTES NOTES (not part of soap proto) Internal Server-Side Calendar Data Model ---------------------------------------- An INVITE is an email message with an iCal attachment. An INVITE has a mail_item_id, which uniquely identifies it, and a UID which identifies the APPOINTMENT it is about....plus other normal iCal data. An Invite has a START and END time -- which corresponds to the range of time where this invite is relevant. Some sample invites: INVITE sent on 3/1 with id=5 UID=1234 for an event happening every Monday with subject "Gorilla Discussion" (START=3/1, END=none) INVITE with id=7 UID=1234 for an exception on 3/21 for the "Left-Handed Gorilla Discussion" (START=3/21 END=3/21) An APPOINTMENT consists of one or more INVITES in the same series -- ie that have the same UID. From the appointment you can get the INSTANCES which are the start/end times of each occurence. Sample Appointment: APPOINTMENT UID=1234 (two INVITES above) ...Instances on every monday with name "Gorilla Discussion" EXCEPT for the 21st, where we talk about lefties instead. An INSTANCE is simply a start-end time and a pointer to an INVITE which has detailed information about the meeting (description, etc) Instances: 3/1 "Gorilla Discussion" id=5 3/14 "Gorilla Discussion" id=5 3/21 "Left-Handed Gorilla Discussion" id=7 TIMEZONES: ---------------- Each iCalendar object can define custom time zone names. For our own web client, there is a list of well-known time zones. These are stored in LDAP. See conf/ldap/zimbra.ldif for their definition. The time zone ID is the cn attribute. TASKS: ---------------- For every AppointmentRequest, there is TaskRequest, and corresponding responses. For GetApptSummariesRequest, there is GetTaskSummariesRequest. GetCalendarItemsSummariesRequest is there for retrieving both appointments and tasks in one shot. Wherever element shows up, the corresponding task request/response will use element. element uses apptID for backward compatibility reason. New code should ignore apptID and use calItemID instead. element uses calItemID only.