~ubuntu-branches/ubuntu/maverick/evolution-data-server/maverick-proposed

« back to all changes in this revision

Viewing changes to servers/exchange/lib/e2k-freebusy.c

  • Committer: Bazaar Package Importer
  • Author(s): Didier Roche
  • Date: 2010-05-17 17:02:06 UTC
  • mfrom: (1.1.79 upstream) (1.6.12 experimental)
  • Revision ID: james.westby@ubuntu.com-20100517170206-4ufr52vwrhh26yh0
Tags: 2.30.1-1ubuntu1
* Merge from debian experimental. Remaining change:
  (LP: #42199, #229669, #173703, #360344, #508494)
  + debian/control:
    - add Vcs-Bzr tag
    - don't use libgnome
    - Use Breaks instead of Conflicts against evolution 2.25 and earlier.
  + debian/evolution-data-server.install,
    debian/patches/45_libcamel_providers_version.patch:
    - use the upstream versioning, not a Debian-specific one 
  + debian/libedata-book1.2-dev.install, debian/libebackend-1.2-dev.install,
    debian/libcamel1.2-dev.install, debian/libedataserverui1.2-dev.install:
    - install html documentation
  + debian/rules:
    - don't build documentation it's shipped with the tarball

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2
 
 
3
 
/* Copyright (C) 2002-2004 Novell, Inc.
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of version 2 of the GNU Lesser General Public
7
 
 * License as published by the Free Software Foundation.
8
 
 *
9
 
 * This program is distributed in the hope that it will be useful,
10
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
 
 * General Public License for more details.
13
 
 *
14
 
 * You should have received a copy of the GNU Lesser General Public
15
 
 * License along with this program; if not, write to the
16
 
 * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17
 
 * Boston, MA 02110-1301, USA.
18
 
 */
19
 
 
20
 
/* e2k-freebusy.c: routines for manipulating Exchange free/busy data */
21
 
 
22
 
#ifdef HAVE_CONFIG_H
23
 
#include <config.h>
24
 
#endif
25
 
 
26
 
#include "e2k-freebusy.h"
27
 
#include "e2k-propnames.h"
28
 
#include "e2k-restriction.h"
29
 
#include "e2k-uri.h"
30
 
#include "e2k-utils.h"
31
 
 
32
 
#include <stdlib.h>
33
 
#include <string.h>
34
 
 
35
 
#include <libedataserver/e-time-utils.h>
36
 
 
37
 
/**
38
 
 * e2k_freebusy_destroy:
39
 
 * @fb: the #E2kFreebusy
40
 
 *
41
 
 * Frees @fb and all associated data.
42
 
 **/
43
 
void
44
 
e2k_freebusy_destroy (E2kFreebusy *fb)
45
 
{
46
 
        gint i;
47
 
 
48
 
        g_object_unref (fb->ctx);
49
 
        for (i = 0; i < E2K_BUSYSTATUS_MAX; i++)
50
 
                g_array_free (fb->events[i], TRUE);
51
 
        g_free (fb->uri);
52
 
        g_free (fb->dn);
53
 
        g_free (fb);
54
 
}
55
 
 
56
 
static gchar *
57
 
fb_uri_for_dn (const gchar *public_uri, const gchar *dn)
58
 
{
59
 
        gchar *uri, *div, *org;
60
 
        GString *str;
61
 
 
62
 
        for (div = strchr (dn, '/'); div; div = strchr (div + 1, '/')) {
63
 
                if (!g_ascii_strncasecmp (div, "/cn=", 4))
64
 
                        break;
65
 
        }
66
 
        g_return_val_if_fail (div, NULL);
67
 
 
68
 
        org = g_strndup (dn, div - dn);
69
 
 
70
 
        str = g_string_new (public_uri);
71
 
        g_string_append (str, "/NON_IPM_SUBTREE/SCHEDULE%2B%20FREE%20BUSY/EX:");
72
 
        e2k_uri_append_encoded (str, org, TRUE, NULL);
73
 
        g_string_append (str, "/USER-");
74
 
        e2k_uri_append_encoded (str, div, TRUE, NULL);
75
 
        g_string_append (str, ".EML");
76
 
 
77
 
        uri = str->str;
78
 
        g_string_free (str, FALSE);
79
 
        g_free (org);
80
 
 
81
 
        return uri;
82
 
}
83
 
 
84
 
static void
85
 
merge_events (GArray *events)
86
 
{
87
 
        E2kFreebusyEvent evt, evt2;
88
 
        gint i;
89
 
 
90
 
        if (events->len < 2)
91
 
                return;
92
 
 
93
 
        evt = g_array_index (events, E2kFreebusyEvent, 0);
94
 
        for (i = 1; i < events->len; i++) {
95
 
                evt2 = g_array_index (events, E2kFreebusyEvent, i);
96
 
                if (evt.end >= evt2.start) {
97
 
                        if (evt2.end > evt.end)
98
 
                                evt.end = evt2.end;
99
 
                        g_array_remove_index (events, i--);
100
 
                } else
101
 
                        evt = evt2;
102
 
        }
103
 
}
104
 
 
105
 
static void
106
 
add_data_for_status (E2kFreebusy *fb, GPtrArray *monthyears, GPtrArray *fbdatas, GArray *events)
107
 
{
108
 
        E2kFreebusyEvent evt;
109
 
        gint i, monthyear;
110
 
        GByteArray *fbdata;
111
 
        guchar *p;
112
 
        struct tm tm;
113
 
 
114
 
        if (!monthyears || !fbdatas)
115
 
                return;
116
 
 
117
 
        memset (&tm, 0, sizeof (tm));
118
 
        for (i = 0; i < monthyears->len && i < fbdatas->len; i++) {
119
 
                monthyear = atoi (monthyears->pdata[i]);
120
 
                fbdata = fbdatas->pdata[i];
121
 
 
122
 
                tm.tm_year = (monthyear >> 4) - 1900;
123
 
                tm.tm_mon = (monthyear & 0xF) - 1;
124
 
 
125
 
                for (p = fbdata->data; p + 3 < fbdata->data + fbdata->len; p += 4) {
126
 
                        tm.tm_mday = 1;
127
 
                        tm.tm_hour = 0;
128
 
                        tm.tm_min = p[0] + p[1] * 256;
129
 
                        evt.start = e_mktime_utc (&tm);
130
 
 
131
 
                        tm.tm_mday = 1;
132
 
                        tm.tm_hour = 0;
133
 
                        tm.tm_min = p[2] + p[3] * 256;
134
 
                        evt.end = e_mktime_utc (&tm);
135
 
 
136
 
                        g_array_append_val (events, evt);
137
 
                }
138
 
        }
139
 
        merge_events (events);
140
 
}
141
 
 
142
 
static const gchar *public_freebusy_props[] = {
143
 
        PR_FREEBUSY_START_RANGE,
144
 
        PR_FREEBUSY_END_RANGE,
145
 
        PR_FREEBUSY_ALL_MONTHS,
146
 
        PR_FREEBUSY_ALL_EVENTS,
147
 
        PR_FREEBUSY_TENTATIVE_MONTHS,
148
 
        PR_FREEBUSY_TENTATIVE_EVENTS,
149
 
        PR_FREEBUSY_BUSY_MONTHS,
150
 
        PR_FREEBUSY_BUSY_EVENTS,
151
 
        PR_FREEBUSY_OOF_MONTHS,
152
 
        PR_FREEBUSY_OOF_EVENTS
153
 
};
154
 
static const gint n_public_freebusy_props = sizeof (public_freebusy_props) / sizeof (public_freebusy_props[0]);
155
 
 
156
 
/**
157
 
 * e2k_freebusy_new:
158
 
 * @ctx: an #E2kContext
159
 
 * @public_uri: the URI of the MAPI public folder tree
160
 
 * @dn: the legacy Exchange DN of a user
161
 
 *
162
 
 * Creates a new #E2kFreebusy, filled in with information from the
163
 
 * indicated user's published free/busy information. This uses the
164
 
 * public free/busy folder; the caller does not need permission to
165
 
 * access the @dn's Calendar.
166
 
 *
167
 
 * Note that currently, this will fail and return %NULL if the user
168
 
 * does not already have free/busy information stored on the server.
169
 
 *
170
 
 * Return value: the freebusy information
171
 
 **/
172
 
E2kFreebusy *
173
 
e2k_freebusy_new (E2kContext *ctx, const gchar *public_uri, const gchar *dn)
174
 
{
175
 
        E2kFreebusy *fb;
176
 
        gchar *uri, *time;
177
 
        GPtrArray *monthyears, *fbdatas;
178
 
        E2kHTTPStatus status;
179
 
        E2kResult *results;
180
 
        gint nresults = 0, i;
181
 
 
182
 
        uri = fb_uri_for_dn (public_uri, dn);
183
 
        g_return_val_if_fail (uri, NULL);
184
 
 
185
 
        status = e2k_context_propfind (ctx, NULL, uri,
186
 
                                       public_freebusy_props,
187
 
                                       n_public_freebusy_props,
188
 
                                       &results, &nresults);
189
 
        if (!E2K_HTTP_STATUS_IS_SUCCESSFUL (status) || nresults == 0) {
190
 
                /* FIXME: create it */
191
 
                g_free (uri);
192
 
                return NULL;
193
 
        }
194
 
 
195
 
        fb = g_new0 (E2kFreebusy, 1);
196
 
        fb->uri = uri;
197
 
        fb->dn = g_strdup (dn);
198
 
        fb->ctx = ctx;
199
 
        g_object_ref (ctx);
200
 
 
201
 
        for (i = 0; i < E2K_BUSYSTATUS_MAX; i++)
202
 
                fb->events[i] = g_array_new (FALSE, FALSE, sizeof (E2kFreebusyEvent));
203
 
 
204
 
        time = e2k_properties_get_prop (
205
 
                results[0].props, PR_FREEBUSY_START_RANGE);
206
 
        fb->start = time ? e2k_systime_to_time_t (strtol (time, NULL, 10)) : 0;
207
 
        time = e2k_properties_get_prop (
208
 
                results[0].props, PR_FREEBUSY_END_RANGE);
209
 
        fb->end = time ? e2k_systime_to_time_t (strtol (time, NULL, 10)) : 0;
210
 
 
211
 
        monthyears = e2k_properties_get_prop (
212
 
                results[0].props, PR_FREEBUSY_ALL_MONTHS);
213
 
        fbdatas = e2k_properties_get_prop (
214
 
                results[0].props, PR_FREEBUSY_ALL_EVENTS);
215
 
        add_data_for_status (fb, monthyears, fbdatas, fb->events[E2K_BUSYSTATUS_ALL]);
216
 
 
217
 
        monthyears = e2k_properties_get_prop (
218
 
                results[0].props, PR_FREEBUSY_TENTATIVE_MONTHS);
219
 
        fbdatas = e2k_properties_get_prop (
220
 
                results[0].props, PR_FREEBUSY_TENTATIVE_EVENTS);
221
 
        add_data_for_status (fb, monthyears, fbdatas, fb->events[E2K_BUSYSTATUS_TENTATIVE]);
222
 
 
223
 
        monthyears = e2k_properties_get_prop (
224
 
                results[0].props, PR_FREEBUSY_BUSY_MONTHS);
225
 
        fbdatas = e2k_properties_get_prop (
226
 
                results[0].props, PR_FREEBUSY_BUSY_EVENTS);
227
 
        add_data_for_status (fb, monthyears, fbdatas, fb->events[E2K_BUSYSTATUS_BUSY]);
228
 
 
229
 
        monthyears = e2k_properties_get_prop (
230
 
                results[0].props, PR_FREEBUSY_OOF_MONTHS);
231
 
        fbdatas = e2k_properties_get_prop (
232
 
                results[0].props, PR_FREEBUSY_OOF_EVENTS);
233
 
        add_data_for_status (fb, monthyears, fbdatas, fb->events[E2K_BUSYSTATUS_OOF]);
234
 
 
235
 
        e2k_results_free (results, nresults);
236
 
        return fb;
237
 
}
238
 
 
239
 
/**
240
 
 * e2k_freebusy_reset:
241
 
 * @fb: an #E2kFreebusy
242
 
 * @nmonths: the number of months of info @fb will store
243
 
 *
244
 
 * Clears all existing data in @fb and resets the start and end times
245
 
 * to a span of @nmonths around the current date.
246
 
 **/
247
 
void
248
 
e2k_freebusy_reset (E2kFreebusy *fb, gint nmonths)
249
 
{
250
 
        time_t now;
251
 
        struct tm tm;
252
 
        gint i;
253
 
 
254
 
        /* Remove all existing events */
255
 
        for (i = 0; i < E2K_BUSYSTATUS_MAX; i++)
256
 
                g_array_set_size (fb->events[i], 0);
257
 
 
258
 
        /* Set the start and end times appropriately: from the beginning
259
 
         * of the current month until nmonths later.
260
 
         * FIXME: Use default timezone, not local time.
261
 
         */
262
 
        now = time (NULL);
263
 
        tm = *gmtime (&now);
264
 
        tm.tm_mday = 1;
265
 
        tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
266
 
 
267
 
        tm.tm_isdst = -1;
268
 
        fb->start = mktime (&tm);
269
 
 
270
 
        tm.tm_mon += nmonths;
271
 
        tm.tm_isdst = -1;
272
 
        fb->end = mktime (&tm);
273
 
}
274
 
 
275
 
/**
276
 
 * e2k_freebusy_add_interval:
277
 
 * @fb: an #E2kFreebusy
278
 
 * @busystatus: the busy status of the interval
279
 
 * @start: the start of the interval
280
 
 * @end: the end of the interval
281
 
 *
282
 
 * This adds an interval of type @busystatus to @fb.
283
 
 **/
284
 
void
285
 
e2k_freebusy_add_interval (E2kFreebusy *fb, E2kBusyStatus busystatus,
286
 
                           time_t start, time_t end)
287
 
{
288
 
        E2kFreebusyEvent evt, *events;
289
 
        gint i;
290
 
 
291
 
        if (busystatus == E2K_BUSYSTATUS_FREE)
292
 
                return;
293
 
 
294
 
        /* Clip to the fb's range */
295
 
        if (start < fb->start)
296
 
                start = fb->start;
297
 
        if (end > fb->end)
298
 
                end = fb->end;
299
 
        if (end <= start)
300
 
                return;
301
 
 
302
 
        events = (E2kFreebusyEvent *)(fb->events[busystatus]->data);
303
 
 
304
 
        for (i = 0; i < fb->events[busystatus]->len; i++) {
305
 
                if (events[i].end >= start)
306
 
                        break;
307
 
        }
308
 
 
309
 
        evt.start = start;
310
 
        evt.end = end;
311
 
 
312
 
        if (i == fb->events[busystatus]->len)
313
 
                g_array_append_val (fb->events[busystatus], evt);
314
 
        else {
315
 
                /* events[i] is the first event that is not completely
316
 
                 * before evt, meaning it is either completely after it,
317
 
                 * or they overlap/abut.
318
 
                 */
319
 
                if (events[i].start > end) {
320
 
                        /* No overlap. Insert evt before events[i]. */
321
 
                        g_array_insert_val (fb->events[busystatus], i, evt);
322
 
                } else {
323
 
                        /* They overlap or abut. Merge them. */
324
 
                        events[i].start = MIN (events[i].start, start);
325
 
                        events[i].end   = MAX (events[i].end, end);
326
 
                }
327
 
        }
328
 
}
329
 
 
330
 
/**
331
 
 * e2k_freebusy_clear_interval:
332
 
 * @fb: an #E2kFreebusy
333
 
 * @start: the start of the interval
334
 
 * @end: the end of the interval
335
 
 *
336
 
 * This removes any events between @start and @end in @fb.
337
 
 **/
338
 
void
339
 
e2k_freebusy_clear_interval (E2kFreebusy *fb, time_t start, time_t end)
340
 
{
341
 
        E2kFreebusyEvent *evt;
342
 
        gint busystatus, i;
343
 
 
344
 
        for (busystatus = 0; busystatus < E2K_BUSYSTATUS_MAX; busystatus++) {
345
 
                for (i = 0; i < fb->events[busystatus]->len; i++) {
346
 
                        evt = &g_array_index (fb->events[busystatus], E2kFreebusyEvent, i);
347
 
                        if (evt->end < start || evt->start > end)
348
 
                                continue;
349
 
 
350
 
                        /* evt overlaps the interval. Truncate or
351
 
                         * remove it.
352
 
                         */
353
 
 
354
 
                        if (evt->start > start /* && evt->start <= end */)
355
 
                                evt->start = end;
356
 
                        if (evt->end < end /* && evt->end >= start */)
357
 
                                evt->end = start;
358
 
 
359
 
                        if (evt->start >= evt->end)
360
 
                                g_array_remove_index (fb->events[busystatus], i--);
361
 
                }
362
 
        }
363
 
}
364
 
 
365
 
static const gchar *freebusy_props[] = {
366
 
        E2K_PR_CALENDAR_DTSTART,
367
 
        E2K_PR_CALENDAR_DTEND,
368
 
        E2K_PR_CALENDAR_BUSY_STATUS
369
 
};
370
 
static const gint n_freebusy_props = sizeof (freebusy_props) / sizeof (freebusy_props[0]);
371
 
 
372
 
/**
373
 
 * e2k_freebusy_add_from_calendar_uri:
374
 
 * @fb: an #E2kFreebusy
375
 
 * @uri: the URI of a calendar folder
376
 
 * @start_tt: start of the range to add
377
 
 * @end_tt: end of the range to add
378
 
 *
379
 
 * This queries the server for events between @start_tt and @end_tt in
380
 
 * the calendar at @uri (which the caller must have permission to
381
 
 * read) and adds them @fb. Any previously-existing events during that
382
 
 * range are removed.
383
 
 *
384
 
 * Return value: an HTTP status code.
385
 
 **/
386
 
E2kHTTPStatus
387
 
e2k_freebusy_add_from_calendar_uri (E2kFreebusy *fb, const gchar *uri,
388
 
                                    time_t start_tt, time_t end_tt)
389
 
{
390
 
        gchar *start, *end, *busystatus;
391
 
        E2kBusyStatus busy;
392
 
        E2kRestriction *rn;
393
 
        E2kResultIter *iter;
394
 
        E2kResult *result;
395
 
 
396
 
        e2k_freebusy_clear_interval (fb, start_tt, end_tt);
397
 
 
398
 
        start = e2k_make_timestamp (start_tt);
399
 
        end = e2k_make_timestamp (end_tt);
400
 
 
401
 
        rn = e2k_restriction_andv (
402
 
                e2k_restriction_prop_string (E2K_PR_DAV_CONTENT_CLASS,
403
 
                                             E2K_RELOP_EQ,
404
 
                                             "urn:content-classes:appointment"),
405
 
                e2k_restriction_prop_date (E2K_PR_CALENDAR_DTEND,
406
 
                                           E2K_RELOP_GT, start),
407
 
                e2k_restriction_prop_date (E2K_PR_CALENDAR_DTSTART,
408
 
                                           E2K_RELOP_LT, end),
409
 
                e2k_restriction_prop_string (E2K_PR_CALENDAR_BUSY_STATUS,
410
 
                                             E2K_RELOP_NE, "FREE"),
411
 
                NULL);
412
 
 
413
 
        iter = e2k_context_search_start (fb->ctx, NULL, uri,
414
 
                                         freebusy_props, n_freebusy_props,
415
 
                                         rn, NULL, TRUE);
416
 
        e2k_restriction_unref (rn);
417
 
        g_free (start);
418
 
        g_free (end);
419
 
 
420
 
        while ((result = e2k_result_iter_next (iter))) {
421
 
                start = e2k_properties_get_prop (result->props,
422
 
                                                 E2K_PR_CALENDAR_DTSTART);
423
 
                end = e2k_properties_get_prop (result->props,
424
 
                                               E2K_PR_CALENDAR_DTEND);
425
 
                busystatus = e2k_properties_get_prop (result->props,
426
 
                                                      E2K_PR_CALENDAR_BUSY_STATUS);
427
 
                if (!start || !end || !busystatus)
428
 
                        continue;
429
 
 
430
 
                if (!strcmp (busystatus, "TENTATIVE"))
431
 
                        busy = E2K_BUSYSTATUS_TENTATIVE;
432
 
                else if (!strcmp (busystatus, "OUTOFOFFICE"))
433
 
                        busy = E2K_BUSYSTATUS_OOF;
434
 
                else
435
 
                        busy = E2K_BUSYSTATUS_BUSY;
436
 
 
437
 
                e2k_freebusy_add_interval (fb, busy,
438
 
                                           e2k_parse_timestamp (start),
439
 
                                           e2k_parse_timestamp (end));
440
 
 
441
 
        }
442
 
 
443
 
        return e2k_result_iter_free (iter);
444
 
}
445
 
 
446
 
static void
447
 
add_events (GArray *events_array, E2kProperties *props,
448
 
            const gchar *month_list_prop, const gchar *data_list_prop)
449
 
{
450
 
        E2kFreebusyEvent *events = (E2kFreebusyEvent *)events_array->data;
451
 
        gint i, evt_start, evt_end, monthyear;
452
 
        struct tm start_tm, end_tm;
453
 
        time_t start, end;
454
 
        GPtrArray *monthyears, *datas;
455
 
        GByteArray *data;
456
 
        gchar startend[4];
457
 
 
458
 
        if (!events_array->len) {
459
 
                e2k_properties_remove (props, month_list_prop);
460
 
                e2k_properties_remove (props, data_list_prop);
461
 
                return;
462
 
        }
463
 
 
464
 
        monthyears = g_ptr_array_new ();
465
 
        start_tm = *gmtime (&events[0].start);
466
 
        end_tm = *gmtime (&events[events_array->len - 1].end);
467
 
        while (start_tm.tm_year <= end_tm.tm_year ||
468
 
               start_tm.tm_mon <= end_tm.tm_mon) {
469
 
                monthyear = ((start_tm.tm_year + 1900) * 16) +
470
 
                        (start_tm.tm_mon + 1);
471
 
                g_ptr_array_add (monthyears, g_strdup_printf ("%d", monthyear));
472
 
 
473
 
                start_tm.tm_mon++;
474
 
                if (start_tm.tm_mon == 12) {
475
 
                        start_tm.tm_year++;
476
 
                        start_tm.tm_mon = 0;
477
 
                }
478
 
        }
479
 
        e2k_properties_set_int_array (props, month_list_prop, monthyears);
480
 
 
481
 
        datas = g_ptr_array_new ();
482
 
        start = events[0].start;
483
 
        i = 0;
484
 
        while (i < events_array->len) {
485
 
                start_tm = *gmtime (&start);
486
 
                start_tm.tm_mon++;
487
 
                end = e_mktime_utc (&start_tm);
488
 
 
489
 
                data = g_byte_array_new ();
490
 
                while (i << events_array->len &&
491
 
                       events[i].end > start && events[i].start < end) {
492
 
                        if (events[i].start < start)
493
 
                                evt_start = 0;
494
 
                        else
495
 
                                evt_start = (events[i].start - start) / 60;
496
 
                        if (events[i].end > end)
497
 
                                evt_end = (end - start) / 60;
498
 
                        else
499
 
                                evt_end = (events[i].end - start) / 60;
500
 
 
501
 
                        startend[0] = evt_start & 0xFF;
502
 
                        startend[1] = evt_start >> 8;
503
 
                        startend[2] = evt_end & 0xFF;
504
 
                        startend[3] = evt_end >> 8;
505
 
                        g_byte_array_append (data, (guint8 *) startend, 4);
506
 
                        i++;
507
 
                }
508
 
 
509
 
                g_ptr_array_add (datas, data);
510
 
                start = end;
511
 
        }
512
 
        e2k_properties_set_binary_array (props, data_list_prop, datas);
513
 
}
514
 
 
515
 
/**
516
 
 * e2k_freebusy_save:
517
 
 * @fb: an #E2kFreebusy
518
 
 *
519
 
 * Saves the data in @fb back to the server.
520
 
 *
521
 
 * Return value: a libsoup or HTTP status code
522
 
 **/
523
 
E2kHTTPStatus
524
 
e2k_freebusy_save (E2kFreebusy *fb)
525
 
{
526
 
        E2kProperties *props;
527
 
        gchar *timestamp;
528
 
        E2kHTTPStatus status;
529
 
 
530
 
        props = e2k_properties_new ();
531
 
        e2k_properties_set_string (props, E2K_PR_EXCHANGE_MESSAGE_CLASS,
532
 
                                   g_strdup ("IPM.Post"));
533
 
        e2k_properties_set_int (props, PR_FREEBUSY_START_RANGE, fb->start);
534
 
        e2k_properties_set_int (props, PR_FREEBUSY_END_RANGE, fb->end);
535
 
        e2k_properties_set_string (props, PR_FREEBUSY_EMAIL_ADDRESS,
536
 
                                   g_strdup (fb->dn));
537
 
 
538
 
        add_events (fb->events[E2K_BUSYSTATUS_ALL], props,
539
 
                    PR_FREEBUSY_ALL_MONTHS, PR_FREEBUSY_ALL_EVENTS);
540
 
        add_events (fb->events[E2K_BUSYSTATUS_TENTATIVE], props,
541
 
                    PR_FREEBUSY_TENTATIVE_MONTHS, PR_FREEBUSY_TENTATIVE_EVENTS);
542
 
        add_events (fb->events[E2K_BUSYSTATUS_BUSY], props,
543
 
                    PR_FREEBUSY_BUSY_MONTHS, PR_FREEBUSY_BUSY_EVENTS);
544
 
        add_events (fb->events[E2K_BUSYSTATUS_OOF], props,
545
 
                    PR_FREEBUSY_OOF_MONTHS, PR_FREEBUSY_OOF_EVENTS);
546
 
 
547
 
        timestamp = e2k_make_timestamp (e2k_context_get_last_timestamp (fb->ctx));
548
 
        e2k_properties_set_date (props, PR_FREEBUSY_LAST_MODIFIED, timestamp);
549
 
 
550
 
        status = e2k_context_proppatch (fb->ctx, NULL, fb->uri, props,
551
 
                                        TRUE, NULL);
552
 
        e2k_properties_free (props);
553
 
 
554
 
        return status;
555
 
}