Free/Busy Searches

The OWA XML Free/Busy interface

In Connector versions up to and including 2.0, we get free/busy data by using the XML interface to OWA. The request URL looks like:

	/public/?Cmd=freebusy
		&start=2002-10-23T04:00:00Z
		&end=2002-11-27T05:00:00Z
		&interval=30
		&u=SMTP:danw@xcs.ximian.com
		&u=SMTP:eleanor@xcs.ximian.com

(Line breaks inserted for clarity. The actual URL would all be on one line of course.) This must be sent with a proper User-Agent header to make it believe you are IE (or you'll get the HTML form instead.) You can add as many users as you like.

The response (with a status of 200 OK) looks like:

	<a:response xmlns:a="WM">
	  <a:recipients>
	    <a:item>
	      <a:displayname>All Attendees</a:displayname>
	      <a:type>1</a:type>
	      <a:fbdata>00000022220000000000000001221000222222...</a:fbdata>
	    </a:item>
	    <a:item>
	      <a:displayname>Dan Winship</a:displayname>
	      <a:email type="SMTP">danw@xcs.ximian.com</a:email>
	      <a:type>1</a:type>
	      <a:fbdata>00000000000000000000000000220000222222...</a:fbdata>
	    </a:item>
	    <a:item>
	      <a:displayname>Eleanor Garcia</a:displayname>
	      <a:email type="SMTP">eleanor@xcs.ximian.com</a:email>
	      <a:type>1</a:type>
	      <a:fbdata>00000022220000000000000001111000000000...</a:fbdata>
	    </a:item>
	  </a:recipients>  
	</a:response>

Each character in the fbdata section represents a length of "interval" minutes (the interval specified in the request URL), with the following mapping:

0

Free

1

Tentative

2

Busy

3

Out of Office

The Lower-level Free/Busy Interface

Note

There is some code in e2k-freebusy.c that works with these interfaces, but it is not very heavily tested and is not currently used by Connector.

Public Free/Busy file

OWA's free/busy data is generated from the data in /public/NON_IPM_SUBTREE/SCHEDULE%2B%20FREE%20BUSY/. Under that directory, there is a directory for each Exchange organization. Eg:

EX:_xF8FF_o=Ximian%20XCS_xF8FF_ou=XCS

In that directory is a file for each user, eg:

USER-_xF8FF_CN=RECIPIENTS_xF8FF_CN=DANW.EML

So, given that my legacyExchangeDN is

/o=Ximian XCS/ou=XCS/cn=Recipients/cn=danw

you can find my public free/busy file. ("_xF8FF_" is the URI-encoded form of '/' when it's not a path separator. e2k_uri_encode handles this.). The legacyExchangeDN is also stored as the PR_FREEBUSY_EMAIL_ADDRESS property on the message, so we can also just search for that.

The PR_FREEBUSY_START_RANGE and PR_FREEBUSY_END_RANGE properties on this message give the start and end times of the published range, in systime (minutes since 16010101T00000000Z) format. These always fall on month boundaries. (The start time is always the beginning of the current month, and the end time is determined by the PR_FREEBUSY_NUM_MONTHS property on the private free/busy file (described below).)

Four pairs of multivalued properties give the actual free/busy data.

PR_FREEBUSY_ALL_MONTHS / PR_FREEBUSY_ALL_EVENTS

all events

PR_FREEBUSY_TENTATIVE_MONTHS / PR_FREEBUSY_TENTATIVE_EVENTS

tentative events

PR_FREEBUSY_BUSY_MONTHS / PR_FREEBUSY_BUSY_EVENTS

busy events

PR_FREEBUSY_OOF_MONTHS / PR_FREEBUSY_OOF_EVENTS

out-of-office events

The _MONTHS properties are arrays of

struct {
	guint month:4;	/* 1 = January */
	guint year :28;
};

Each element of this array has a corresponding element in the _EVENTS array containing the events in that month and year as an array of:

struct {
	guint16 start;	/* minutes since start of month */
	guint16 end;	/* minutes since start of month */
};

If there are no events of a given type in the given interval, then Outlook leaves the two properties for that type unset. (If there are no events at all, then all 8 properties will be unset.) But when Exchange regenerates them itself, it just creates them empty instead.

Additionally, several other properties are used:

PR_FREEBUSY_LAST_MODIFIED

the last modtime

MAPI proptag 0x68410003

Unknown. (Usually 0)

MAPI proptag 0x6846000b

Unknown. (Usually 1)

There is also one more property, http://schemas.microsoft.com/mapi/string/{8CDA1CAE-AFDB-11D2-9379-00C04FA357AA}/microsoft.cdo.calendar.fbhistory, of type PT_MV_BINARY, which seems to always contain 3 24-byte items. The first two bytes of each are a month-year like the _MONTHS properties (and for an up-to-date free/busy file, the 3 items will be this month, next month, and the month after). There are then 6 0 bytes, followed by 8 bytes of data that are almost always the same in each of the 3 items, followed by 8 0 bytes. The purpose of this data is currently unknown. The middle chunk of data seems to get changed every time the free/busy data is updated. It may be a timestamp of some unknown sort?


Personal Free/Busy file

Each user also has a personal free/busy message, at:

/exchange/username/NON_IPM_SUBTREE/Freebusy%20Data/LocalFreebusy.EML

This is known to be used for at least three things:

  1. The user's free/busy information is stored on it (in a completely different format from the public free/busy file), as described below.

  2. The user's list of delegates and their "see private items" permissions are stored on it, as described in "Delegates".

  3. Certain preferences that allow the account to be used as a "resource" are stored here, as described in "Direct Booking".

When you create an event via WebDAV, the server will eventually update the free/busy file with that data, but this is subject to various delays and exceptions that we don't understand. Among other things, Outlook uses the free/busy file to decide what dates to make bold in the minicalendar, which is why Connector-created appointments don't always show up there right away even when Outlook does display the event itself.

The free/busy data is stored in the PR_PERSONAL_FREEBUSY property as follows:

struct {
	guint32 magic;		/* 0xdeadbeef */
	guint32 header_len;	/* 0x00000040 */
	guint32 unknown_1;	/* 0x00010000 */
	guint32 unknown_2;	/* 0x00000000 */

	guint32 num_groups;	/* number of free/busy groups */
	guint32 num_events;	/* total number of free/busy objects */
	guint32 unknown_3;	/* 0x00000008 */
	guint32 unknown_4;	/* 0x00000004 */

	guint64 first_event;	/* Windows filetime of start of first event */
	guint64 last_event;	/* Windows filetime of end of last event */
	guint64 start_valid;	/* Windows filetime of start of range */
	guint64 end_valid;	/* Windows filetime of end of range */

	struct {
		guint32 index;	/* Index of first event belonging to this group */
		guint32 count;	/* Number of events in this group */
	} groups[num_groups];

	struct {
		guint32 offset;	/* Offset in minutes of event from start of group */
		guint len:13;	/* Length of event in minutes */
		guint status:3;	/* Status (1 = tentative, 2 = busy, 3 = oof) */
	} events[num_events];
};

The first group starts at the time indicated by start_valid, so an event in group 0 with an offset of 0 starts at start_valid. Group N starts at time (start_valid + N * 0x10000). If there are no events in the timeframe of a group, its index and count will both be 0.

There is no particular correlation between free/busy events and events on the calendar: two adjacent calendar events can be stored as a single free/busy event, and a given calendar event may be split across two or more free/busy groups.

The number of months covered by the free/busy data is in PR_FREEBUSY_NUM_MONTHS.

A posting on the MAPI-L list claimed that setting PR_DISABLE_FULL_FIDELITY to 0 on the free/busy message will force Outlook to regenerate it, but I haven't tested this.