"Fossies" - the Fresh Open Source Software Archive 
Member "evolution-mapi-3.46.1/src/libexchangemapi/e-mapi-cal-recur-utils.c" (2 Dec 2022, 46079 Bytes) of package /linux/misc/evolution-mapi-3.46.1.tar.xz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "e-mapi-cal-recur-utils.c" see the
Fossies "Dox" file reference documentation.
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3 * This program is free software; you can redistribute it and/or
4 * modify it under the terms of the GNU Lesser General Public
5 * License as published by the Free Software Foundation; either
6 * version 2 of the License, or (at your option) version 3.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * Lesser General Public License for more details.
12 *
13 * You should have received a copy of the GNU Lesser General Public
14 * License along with the program; if not, see <http://www.gnu.org/licenses/>
15 *
16 *
17 * Authors:
18 * Suman Manjunath <msuman@novell.com>
19 *
20 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
21 *
22 */
23
24 #include "evolution-mapi-config.h"
25
26 #include <libecal/libecal.h>
27
28 #include "e-mapi-cal-utils.h"
29 #include "e-mapi-cal-recur-utils.h"
30
31 /* Reader/Writer versions */
32 #define READER_VERSION 0x3004
33 #define WRITER_VERSION 0x3004
34 #define READER_VERSION2 0x3006
35 #define WRITER_VERSION2 0x3009
36
37 /* Override flags defining what fields might be found in ExceptionInfo */
38 #define ARO_SUBJECT 0x0001
39 #define ARO_MEETINGTYPE 0x0002
40 #define ARO_REMINDERDELTA 0x0004
41 #define ARO_REMINDER 0x0008
42 #define ARO_LOCATION 0x0010
43 #define ARO_BUSYSTATUS 0x0020
44 #define ARO_ATTACHMENT 0x0040
45 #define ARO_SUBTYPE 0x0080
46 #define ARO_APPTCOLOR 0x0100
47 #define ARO_EXCEPTIONAL_BODY 0x0200
48
49 /* Serialization helper: append len bytes from var to arr. */
50 #define GBA_APPEND(a, v, l) g_byte_array_append ((a), (guint8*)(v), (l))
51
52 /* Serialization helper: append the value of the variable to arr. */
53 #define GBA_APPEND_LVAL(a, v) GBA_APPEND ((a), (&v), (sizeof (v)))
54
55 /* Unserialization helper: read len bytes into buff from ba at offset off. */
56 #define GBA_MEMCPY_OFFSET(arr, off, buf, blen) \
57 G_STMT_START { \
58 g_return_val_if_fail ((off >= 0 && arr->len - off >= blen), FALSE); \
59 memcpy (buf, arr->data + off, blen); \
60 off += blen; \
61 } G_STMT_END
62
63 /* Unserialization helper: dereference and increment pointer. */
64 #define GBA_DEREF_OFFSET(arr, off, lval, valtype) \
65 G_STMT_START { \
66 g_return_val_if_fail ((off >= 0 && arr->len - off >= sizeof (valtype)), FALSE); \
67 lval = *((valtype*)(arr->data+off)); \
68 off += sizeof (valtype); \
69 } G_STMT_END
70
71 /** MS-OXOCAL 2.2.1.44.3 */
72 struct ema_ChangeHighlight {
73 guint32 ChangeHighlightSize;
74 guint32 ChangeHighlightValue;
75 void *Reserved;
76 };
77
78 /** MS-OXOCAL 2.2.1.44.4 */
79 struct ema_ExtendedException {
80 struct ema_ChangeHighlight ChangeHighlight;
81 guint32 ReservedBlockEE1Size;
82 void *ReservedBlockEE1;
83 guint32 StartDateTime;
84 guint32 EndDateTime;
85 guint32 OriginalStartDate;
86 guint16 WideCharSubjectLength;
87 gchar *WideCharSubject;
88 guint16 WideCharLocationLength;
89 gchar *WideCharLocation;
90 guint32 ReservedBlockEE2Size;
91 void *ReservedBlockEE2;
92 };
93
94 /** MS-OXOCAL 2.2.1.44.2 */
95 struct ema_ExceptionInfo {
96 guint32 StartDateTime;
97 guint32 EndDateTime;
98 guint32 OriginalStartDate;
99 guint16 OverrideFlags;
100 guint16 SubjectLength;
101 guint16 SubjectLength2;
102 gchar *Subject;
103 guint32 MeetingType;
104 guint32 ReminderDelta;
105 guint32 ReminderSet;
106 guint16 LocationLength;
107 guint16 LocationLength2;
108 gchar *Location;
109 guint32 BusyStatus;
110 guint32 Attachment;
111 guint32 SubType;
112 guint32 AppointmentColor;
113 };
114
115 /** MS-OXOCAL 2.2.1.44.1 */
116 struct ema_RecurrencePattern {
117 guint16 ReaderVersion;
118 guint16 WriterVersion;
119 guint16 RecurFrequency;
120 guint16 PatternType;
121 guint16 CalendarType;
122 guint32 FirstDateTime;
123 guint32 Period;
124 guint32 SlidingFlag;
125 guint32 PatternTypeSpecific;
126 guint32 N;
127 guint32 EndType;
128 guint32 OccurrenceCount;
129 guint32 FirstDOW;
130 guint32 DeletedInstanceCount;
131 guint32 *DeletedInstanceDates;
132 guint32 ModifiedInstanceCount;
133 guint32 *ModifiedInstanceDates;
134 guint32 StartDate;
135 guint32 EndDate;
136 };
137
138 /** MS-OXOCAL 2.2.1.44.5 */
139 struct ema_AppointmentRecurrencePattern {
140 struct ema_RecurrencePattern RecurrencePattern;
141 guint32 ReaderVersion2;
142 guint32 WriterVersion2;
143 guint32 StartTimeOffset;
144 guint32 EndTimeOffset;
145 guint16 ExceptionCount;
146 struct ema_ExceptionInfo *ExceptionInfo;
147 guint32 ReservedBlock1Size;
148 void *ReservedBlock1;
149 struct ema_ExtendedException *ExtendedException;
150 guint32 ReservedBlock2Size;
151 void *ReservedBlock2;
152 };
153
154 /** Serialize a RecurrencePattern to the end of an existing GByteArray */
155 static void
156 rp_to_gba(const struct ema_RecurrencePattern *rp, GByteArray *gba)
157 {
158 GBA_APPEND_LVAL (gba, rp->ReaderVersion);
159 GBA_APPEND_LVAL (gba, rp->WriterVersion);
160 GBA_APPEND_LVAL (gba, rp->RecurFrequency);
161 GBA_APPEND_LVAL (gba, rp->PatternType);
162 GBA_APPEND_LVAL (gba, rp->CalendarType);
163 GBA_APPEND_LVAL (gba, rp->FirstDateTime);
164 GBA_APPEND_LVAL (gba, rp->Period);
165 GBA_APPEND_LVAL (gba, rp->SlidingFlag);
166
167 if (rp->PatternType != PatternType_Day) {
168 GBA_APPEND_LVAL (gba, rp->PatternTypeSpecific);
169 if (rp->PatternType == PatternType_MonthNth) {
170 GBA_APPEND_LVAL (gba, rp->N);
171 }
172 }
173
174 GBA_APPEND_LVAL (gba, rp->EndType);
175 GBA_APPEND_LVAL (gba, rp->OccurrenceCount);
176 GBA_APPEND_LVAL (gba, rp->FirstDOW);
177 GBA_APPEND_LVAL (gba, rp->DeletedInstanceCount);
178 if ( rp->DeletedInstanceCount ) {
179 GBA_APPEND (gba, rp->DeletedInstanceDates,
180 sizeof (guint32) * rp->DeletedInstanceCount);
181 }
182 GBA_APPEND_LVAL(gba, rp->ModifiedInstanceCount);
183 if ( rp->ModifiedInstanceCount ) {
184 GBA_APPEND (gba, rp->ModifiedInstanceDates,
185 sizeof (guint32) * rp->ModifiedInstanceCount);
186 }
187 GBA_APPEND_LVAL (gba, rp->StartDate);
188 GBA_APPEND_LVAL (gba, rp->EndDate);
189 }
190
191 static void
192 ei_to_gba(const struct ema_ExceptionInfo *ei, GByteArray *gba)
193 {
194 GBA_APPEND_LVAL (gba, ei->StartDateTime);
195 GBA_APPEND_LVAL (gba, ei->EndDateTime);
196 GBA_APPEND_LVAL (gba, ei->OriginalStartDate);
197 GBA_APPEND_LVAL (gba, ei->OverrideFlags);
198 if (ei->OverrideFlags&ARO_SUBJECT) {
199 GBA_APPEND_LVAL (gba, ei->SubjectLength);
200 GBA_APPEND_LVAL (gba, ei->SubjectLength2);
201 GBA_APPEND (gba, ei->Subject, strlen (ei->Subject));
202 }
203 if (ei->OverrideFlags&ARO_MEETINGTYPE) {
204 GBA_APPEND_LVAL (gba, ei->MeetingType);
205 }
206 if (ei->OverrideFlags&ARO_REMINDERDELTA) {
207 GBA_APPEND_LVAL (gba, ei->ReminderDelta);
208 }
209 if (ei->OverrideFlags & ARO_REMINDER) {
210 GBA_APPEND_LVAL (gba, ei->ReminderSet);
211 }
212 if (ei->OverrideFlags&ARO_LOCATION) {
213 GBA_APPEND_LVAL (gba, ei->LocationLength);
214 GBA_APPEND_LVAL (gba, ei->LocationLength2);
215 GBA_APPEND (gba, ei->Location, strlen (ei->Location));
216 }
217 if (ei->OverrideFlags&ARO_BUSYSTATUS) {
218 GBA_APPEND_LVAL (gba, ei->BusyStatus);
219 }
220 if (ei->OverrideFlags&ARO_ATTACHMENT) {
221 GBA_APPEND_LVAL (gba, ei->Attachment);
222 }
223 if (ei->OverrideFlags&ARO_SUBTYPE) {
224 GBA_APPEND_LVAL (gba, ei->SubType);
225 }
226 if (ei->OverrideFlags&ARO_APPTCOLOR) {
227 GBA_APPEND_LVAL (gba, ei->AppointmentColor);
228 }
229 }
230
231 static void
232 ee_to_gba(const struct ema_ExtendedException *ee,
233 const struct ema_AppointmentRecurrencePattern *arp, int exnum,
234 GByteArray *gba)
235 {
236 if (arp->WriterVersion2 >= 0x3009) {
237 GBA_APPEND_LVAL (gba, ee->ChangeHighlight.ChangeHighlightSize);
238 if (ee->ChangeHighlight.ChangeHighlightSize >= sizeof (guint32)) {
239 GBA_APPEND_LVAL (gba, ee->ChangeHighlight.ChangeHighlightValue);
240 if (ee->ChangeHighlight.ChangeHighlightSize > sizeof (guint32)) {
241 GBA_APPEND (gba, ee->ChangeHighlight.Reserved,
242 ee->ChangeHighlight.ChangeHighlightSize - sizeof (guint32));
243 }
244 }
245 }
246
247 GBA_APPEND_LVAL (gba, ee->ReservedBlockEE1Size);
248 if (ee->ReservedBlockEE1Size) {
249 GBA_APPEND (gba, ee->ReservedBlockEE1, ee->ReservedBlockEE1Size);
250 }
251
252 if (arp->ExceptionInfo[exnum].OverrideFlags&(ARO_SUBJECT|ARO_LOCATION)) {
253 GBA_APPEND_LVAL (gba, ee->StartDateTime);
254 GBA_APPEND_LVAL (gba, ee->EndDateTime);
255 GBA_APPEND_LVAL (gba, ee->OriginalStartDate);
256
257 if (arp->ExceptionInfo[exnum].OverrideFlags&ARO_SUBJECT) {
258 GBA_APPEND_LVAL (gba, ee->WideCharSubjectLength);
259 GBA_APPEND (gba, ee->WideCharSubject,
260 sizeof (guint16) * ee->WideCharSubjectLength);
261 }
262
263 if( arp->ExceptionInfo[exnum].OverrideFlags&ARO_LOCATION) {
264 GBA_APPEND_LVAL(gba, ee->WideCharLocationLength);
265 GBA_APPEND(gba, ee->WideCharLocation,
266 sizeof (guint16) * ee->WideCharLocationLength);
267 }
268
269 GBA_APPEND_LVAL (gba, ee->ReservedBlockEE2Size);
270 if (ee->ReservedBlockEE2Size) {
271 GBA_APPEND (gba, ee->ReservedBlockEE2,
272 ee->ReservedBlockEE2Size);
273 }
274 }
275 }
276
277 static void
278 arp_to_gba(const struct ema_AppointmentRecurrencePattern *arp, GByteArray *gba)
279 {
280 int i;
281
282 rp_to_gba (&arp->RecurrencePattern, gba);
283 GBA_APPEND_LVAL (gba, arp->ReaderVersion2);
284 GBA_APPEND_LVAL (gba, arp->WriterVersion2);
285 GBA_APPEND_LVAL (gba, arp->StartTimeOffset);
286 GBA_APPEND_LVAL (gba, arp->EndTimeOffset);
287 GBA_APPEND_LVAL (gba, arp->ExceptionCount);
288 for (i = 0; i < arp->ExceptionCount; ++i) {
289 ei_to_gba (&arp->ExceptionInfo[i], gba);
290 }
291 GBA_APPEND_LVAL (gba, arp->ReservedBlock1Size);
292 if (arp->ReservedBlock1Size) {
293 GBA_APPEND (gba, arp->ReservedBlock1, arp->ReservedBlock1Size);
294 }
295 for (i = 0; i < arp->ExceptionCount; ++i) {
296 ee_to_gba (&arp->ExtendedException[i], arp, i, gba);
297 }
298 GBA_APPEND_LVAL (gba, arp->ReservedBlock2Size);
299 if (arp->ReservedBlock2Size) {
300 GBA_APPEND (gba, arp->ReservedBlock2, arp->ReservedBlock2Size);
301 }
302 }
303
304 static gboolean
305 gba_to_rp(const GByteArray *gba, ptrdiff_t *off,
306 struct ema_RecurrencePattern *rp)
307 {
308 GBA_DEREF_OFFSET (gba, *off, rp->ReaderVersion, guint16);
309 GBA_DEREF_OFFSET (gba, *off, rp->WriterVersion, guint16);
310 GBA_DEREF_OFFSET (gba, *off, rp->RecurFrequency, guint16);
311 GBA_DEREF_OFFSET (gba, *off, rp->PatternType, guint16);
312 GBA_DEREF_OFFSET (gba, *off, rp->CalendarType, guint16);
313 GBA_DEREF_OFFSET (gba, *off, rp->FirstDateTime, guint32);
314 GBA_DEREF_OFFSET (gba, *off, rp->Period, guint32);
315 GBA_DEREF_OFFSET (gba, *off, rp->SlidingFlag, guint32);
316
317 if (rp->PatternType != PatternType_Day) {
318 GBA_DEREF_OFFSET (gba, *off, rp->PatternTypeSpecific, guint32);
319 if (rp->PatternType == PatternType_MonthNth) {
320 GBA_DEREF_OFFSET (gba, *off, rp->N,
321 guint32);
322 }
323 }
324
325 GBA_DEREF_OFFSET (gba, *off, rp->EndType, guint32);
326 GBA_DEREF_OFFSET (gba, *off, rp->OccurrenceCount, guint32);
327 GBA_DEREF_OFFSET (gba, *off, rp->FirstDOW, guint32);
328
329 GBA_DEREF_OFFSET (gba, *off, rp->DeletedInstanceCount, guint32);
330 if (rp->DeletedInstanceCount) {
331 rp->DeletedInstanceDates = g_new (guint32,
332 rp->DeletedInstanceCount);
333 GBA_MEMCPY_OFFSET(gba, *off, rp->DeletedInstanceDates,
334 sizeof (guint32) * rp->DeletedInstanceCount);
335 }
336
337 GBA_DEREF_OFFSET (gba, *off, rp->ModifiedInstanceCount, guint32);
338 if (rp->ModifiedInstanceCount) {
339 rp->ModifiedInstanceDates = g_new (guint32,
340 rp->ModifiedInstanceCount);
341 GBA_MEMCPY_OFFSET (gba, *off, rp->ModifiedInstanceDates,
342 sizeof (guint32) * rp->ModifiedInstanceCount);
343 }
344
345 GBA_DEREF_OFFSET(gba, *off, rp->StartDate, guint32);
346 GBA_DEREF_OFFSET(gba, *off, rp->EndDate, guint32);
347
348 return TRUE;
349 }
350
351 static gboolean
352 gba_to_ei(const GByteArray *gba, ptrdiff_t *off, struct ema_ExceptionInfo *ei)
353 {
354 GBA_DEREF_OFFSET (gba, *off, ei->StartDateTime, guint32);
355 GBA_DEREF_OFFSET (gba, *off, ei->EndDateTime, guint32);
356 GBA_DEREF_OFFSET (gba, *off, ei->OriginalStartDate, guint32);
357 GBA_DEREF_OFFSET (gba, *off, ei->OverrideFlags, guint16);
358
359 if (ei->OverrideFlags&ARO_SUBJECT) {
360 GBA_DEREF_OFFSET (gba, *off, ei->SubjectLength, guint16);
361 GBA_DEREF_OFFSET (gba, *off, ei->SubjectLength2, guint16);
362 ei->Subject = g_new0 (gchar, ei->SubjectLength2 + 1);
363 GBA_MEMCPY_OFFSET (gba, *off, ei->Subject, ei->SubjectLength2);
364 }
365
366 if (ei->OverrideFlags&ARO_MEETINGTYPE) {
367 GBA_DEREF_OFFSET (gba, *off, ei->MeetingType, guint32);
368 }
369
370 if (ei->OverrideFlags&ARO_REMINDERDELTA) {
371 GBA_DEREF_OFFSET (gba, *off, ei->ReminderDelta, guint32);
372 }
373
374 if (ei->OverrideFlags & ARO_REMINDER) {
375 GBA_DEREF_OFFSET (gba, *off, ei->ReminderSet, guint32);
376 }
377
378 if (ei->OverrideFlags&ARO_LOCATION) {
379 GBA_DEREF_OFFSET (gba, *off, ei->LocationLength, guint16);
380 GBA_DEREF_OFFSET (gba, *off, ei->LocationLength2, guint16);
381 ei->Location = g_new0 (gchar, ei->LocationLength2 + 1);
382 GBA_MEMCPY_OFFSET (gba, *off, ei->Location, ei->LocationLength2);
383 }
384
385 if (ei->OverrideFlags&ARO_BUSYSTATUS) {
386 GBA_DEREF_OFFSET (gba, *off, ei->BusyStatus, guint32);
387 }
388
389 if (ei->OverrideFlags&ARO_ATTACHMENT) {
390 GBA_DEREF_OFFSET (gba, *off, ei->Attachment, guint32);
391 }
392
393 if (ei->OverrideFlags&ARO_SUBTYPE) {
394 GBA_DEREF_OFFSET (gba, *off, ei->SubType, guint32);
395 }
396
397 if (ei->OverrideFlags&ARO_APPTCOLOR) {
398 GBA_DEREF_OFFSET (gba, *off, ei->AppointmentColor, guint32);
399 }
400
401 return TRUE;
402 }
403
404 static gboolean
405 gba_to_ee(const GByteArray *gba, ptrdiff_t *off,
406 struct ema_ExtendedException *ee,
407 struct ema_AppointmentRecurrencePattern *arp, int exnum)
408 {
409 GBA_DEREF_OFFSET (gba, *off, ee->ChangeHighlight.ChangeHighlightSize,
410 guint32);
411
412 if (arp->WriterVersion2 >= 0x3009) {
413 if (ee->ChangeHighlight.ChangeHighlightSize > 0) {
414 int reserved_size = ee->ChangeHighlight.ChangeHighlightSize - sizeof (guint32);
415 GBA_DEREF_OFFSET (gba, *off,
416 ee->ChangeHighlight.ChangeHighlightValue,
417 guint32);
418 if (reserved_size > 0) {
419 ee->ChangeHighlight.Reserved = g_new (gchar, reserved_size);
420 GBA_MEMCPY_OFFSET (gba, *off,
421 &ee->ChangeHighlight.Reserved,
422 reserved_size);
423 }
424 }
425 }
426
427 GBA_DEREF_OFFSET (gba, *off, ee->ReservedBlockEE1Size, guint32);
428 if (ee->ReservedBlockEE1Size) {
429 ee->ReservedBlockEE1 = g_new (gchar, ee->ReservedBlockEE1Size);
430 GBA_MEMCPY_OFFSET (gba, *off, ee->ReservedBlockEE1,
431 ee->ReservedBlockEE1Size);
432 }
433
434 if (arp->ExceptionInfo[exnum].OverrideFlags&(ARO_SUBJECT|ARO_LOCATION)) {
435 GBA_DEREF_OFFSET (gba, *off, ee->StartDateTime, guint32);
436 GBA_DEREF_OFFSET (gba, *off, ee->EndDateTime, guint32);
437 GBA_DEREF_OFFSET (gba, *off, ee->OriginalStartDate, guint32);
438
439 if(arp->ExceptionInfo[exnum].OverrideFlags&ARO_SUBJECT) {
440 GBA_DEREF_OFFSET (gba, *off, ee->WideCharSubjectLength,
441 guint16);
442 ee->WideCharSubject = g_new0(gchar,
443 sizeof(guint16) * (ee->WideCharSubjectLength + 1));
444 GBA_MEMCPY_OFFSET (gba, *off, ee->WideCharSubject,
445 sizeof(guint16) * ee->WideCharSubjectLength);
446 }
447
448 if(arp->ExceptionInfo[exnum].OverrideFlags&ARO_LOCATION) {
449 GBA_DEREF_OFFSET (gba, *off, ee->WideCharLocationLength,
450 guint16);
451 ee->WideCharLocation = g_new0 (gchar,
452 sizeof(guint16) * (ee->WideCharLocationLength + 1));
453 GBA_MEMCPY_OFFSET (gba, *off, ee->WideCharLocation,
454 sizeof (guint16) * ee->WideCharLocationLength);
455 }
456
457 GBA_DEREF_OFFSET (gba, *off, ee->ReservedBlockEE2Size, guint32);
458 if (ee->ReservedBlockEE2Size) {
459 ee->ReservedBlockEE2 = g_new (gchar,
460 ee->ReservedBlockEE2Size);
461 GBA_MEMCPY_OFFSET (gba, *off, ee->ReservedBlockEE2,
462 ee->ReservedBlockEE2Size);
463 }
464 }
465
466 return TRUE;
467 }
468
469 static gboolean
470 gba_to_arp(const GByteArray *gba, ptrdiff_t *off,
471 struct ema_AppointmentRecurrencePattern *arp) {
472 int i;
473
474 g_return_val_if_fail (gba_to_rp (gba, off, &arp->RecurrencePattern),
475 FALSE);
476 GBA_DEREF_OFFSET (gba, *off, arp->ReaderVersion2, guint32);
477 GBA_DEREF_OFFSET (gba, *off, arp->WriterVersion2, guint32);
478 GBA_DEREF_OFFSET (gba, *off, arp->StartTimeOffset, guint32);
479 GBA_DEREF_OFFSET (gba, *off, arp->EndTimeOffset, guint32);
480
481 GBA_DEREF_OFFSET (gba, *off, arp->ExceptionCount, guint16);
482 if (arp->ExceptionCount) {
483 arp->ExceptionInfo = g_new0 (struct ema_ExceptionInfo,
484 arp->ExceptionCount);
485 for (i = 0; i < arp->ExceptionCount; ++i) {
486 g_return_val_if_fail (gba_to_ei (gba, off, &arp->ExceptionInfo[i]),
487 FALSE);
488 }
489 }
490
491 GBA_DEREF_OFFSET (gba, *off, arp->ReservedBlock1Size, guint32);
492 if (arp->ReservedBlock1Size) {
493 arp->ReservedBlock1 = g_new (gchar, arp->ReservedBlock1Size);
494 GBA_MEMCPY_OFFSET (gba, *off, arp->ReservedBlock1,
495 arp->ReservedBlock1Size);
496 }
497
498 if (arp->ExceptionCount) {
499 arp->ExtendedException = g_new0 (struct ema_ExtendedException,
500 arp->ExceptionCount);
501 for (i = 0; i < arp->ExceptionCount; ++i) {
502 g_return_val_if_fail (gba_to_ee (gba, off, &arp->ExtendedException[i], arp, i),
503 FALSE);
504 }
505 }
506
507 GBA_DEREF_OFFSET (gba, *off, arp->ReservedBlock2Size, guint32);
508 if (arp->ReservedBlock2Size) {
509 arp->ReservedBlock2 = g_new (gchar, arp->ReservedBlock2Size);
510 GBA_MEMCPY_OFFSET (gba, *off, arp->ReservedBlock2,
511 arp->ReservedBlock2Size);
512 }
513
514 return TRUE;
515 }
516
517 static void
518 free_arp_contents(struct ema_AppointmentRecurrencePattern *arp)
519 {
520 int i;
521
522 if(arp) {
523 if (arp->RecurrencePattern.DeletedInstanceDates)
524 g_free (arp->RecurrencePattern.DeletedInstanceDates);
525 if (arp->RecurrencePattern.ModifiedInstanceDates)
526 g_free (arp->RecurrencePattern.ModifiedInstanceDates);
527 if (arp->ExceptionInfo) {
528 for (i = 0; i < arp->RecurrencePattern.ModifiedInstanceCount; ++i) {
529 if (arp->ExceptionInfo[i].Subject)
530 g_free (arp->ExceptionInfo[i].Subject);
531 if (arp->ExceptionInfo[i].Location)
532 g_free (arp->ExceptionInfo[i].Location);
533 }
534 g_free (arp->ExceptionInfo);
535 }
536 if (arp->ReservedBlock1) {
537 g_free (arp->ReservedBlock1);
538 }
539 if (arp->ExtendedException) {
540 for (i = 0; i < arp->RecurrencePattern.ModifiedInstanceCount; ++i) {
541 if (arp->ExtendedException[i].ChangeHighlight.Reserved)
542 g_free (arp->ExtendedException[i].ChangeHighlight.Reserved);
543 if (arp->ExtendedException[i].ReservedBlockEE1)
544 g_free (arp->ExtendedException[i].ReservedBlockEE1);
545 if (arp->ExtendedException[i].WideCharSubject)
546 g_free (arp->ExtendedException[i].WideCharSubject);
547 if (arp->ExtendedException[i].WideCharLocation)
548 g_free (arp->ExtendedException[i].WideCharLocation);
549 if (arp->ExtendedException[i].ReservedBlockEE2)
550 g_free (arp->ExtendedException[i].ReservedBlockEE2);
551 }
552 g_free (arp->ExtendedException);
553 }
554 if (arp->ReservedBlock2) {
555 g_free (arp->ReservedBlock2);
556 }
557 }
558 }
559
560 static ICalRecurrenceWeekday
561 get_ical_weekstart (uint32_t fdow)
562 {
563 switch (fdow) {
564 case FirstDOW_Sunday:
565 return I_CAL_SUNDAY_WEEKDAY;
566 case FirstDOW_Monday:
567 return I_CAL_MONDAY_WEEKDAY;
568 case FirstDOW_Tuesday:
569 return I_CAL_TUESDAY_WEEKDAY;
570 case FirstDOW_Wednesday:
571 return I_CAL_WEDNESDAY_WEEKDAY;
572 case FirstDOW_Thursday:
573 return I_CAL_THURSDAY_WEEKDAY;
574 case FirstDOW_Friday:
575 return I_CAL_FRIDAY_WEEKDAY;
576 case FirstDOW_Saturday:
577 return I_CAL_SATURDAY_WEEKDAY;
578 default:
579 return I_CAL_SUNDAY_WEEKDAY;
580 }
581 }
582
583 static uint32_t
584 get_mapi_weekstart (ICalRecurrenceWeekday weekstart)
585 {
586 switch (weekstart) {
587 case I_CAL_SUNDAY_WEEKDAY:
588 return FirstDOW_Sunday;
589 case I_CAL_MONDAY_WEEKDAY:
590 return FirstDOW_Monday;
591 case I_CAL_TUESDAY_WEEKDAY:
592 return FirstDOW_Tuesday;
593 case I_CAL_WEDNESDAY_WEEKDAY:
594 return FirstDOW_Wednesday;
595 case I_CAL_THURSDAY_WEEKDAY:
596 return FirstDOW_Thursday;
597 case I_CAL_FRIDAY_WEEKDAY:
598 return FirstDOW_Friday;
599 case I_CAL_SATURDAY_WEEKDAY:
600 return FirstDOW_Saturday;
601 default:
602 return FirstDOW_Sunday;
603 }
604 }
605
606 static uint32_t
607 get_mapi_day (ICalRecurrenceWeekday someday)
608 {
609 switch (someday) {
610 case I_CAL_SUNDAY_WEEKDAY:
611 return olSunday;
612 case I_CAL_MONDAY_WEEKDAY:
613 return olMonday;
614 case I_CAL_TUESDAY_WEEKDAY:
615 return olTuesday;
616 case I_CAL_WEDNESDAY_WEEKDAY:
617 return olWednesday;
618 case I_CAL_THURSDAY_WEEKDAY:
619 return olThursday;
620 case I_CAL_FRIDAY_WEEKDAY:
621 return olFriday;
622 case I_CAL_SATURDAY_WEEKDAY:
623 return olSaturday;
624 default:
625 return 0;
626 }
627 }
628
629 static int32_t
630 get_ical_pos (uint32_t pos)
631 {
632 switch (pos) {
633 case RecurrenceN_First : return 1;
634 case RecurrenceN_Second : return 2;
635 case RecurrenceN_Third : return 3;
636 case RecurrenceN_Fourth : return 4;
637 case RecurrenceN_Last : return (-1);
638 default : return 0;
639 }
640 }
641
642 static uint32_t
643 get_mapi_pos (int32_t pos)
644 {
645 switch (pos) {
646 case 1 : return RecurrenceN_First;
647 case 2 : return RecurrenceN_Second;
648 case 3 : return RecurrenceN_Third;
649 case 4 : return RecurrenceN_Fourth;
650 case -1 : return RecurrenceN_Last;
651 default : return 0;
652 }
653 }
654
655 #define cFileTimeUnitsPerSecond 10000000
656
657 #if 0
658 static void
659 convert_recurrence_minutes_to_date (uint32_t minutes, struct FILETIME *ft)
660 {
661 NTTIME nt;
662
663 nt = (NTTIME) minutes * (60 * cFileTimeUnitsPerSecond);
664
665 ft->dwLowDateTime = (uint32_t)((nt << 32) >> 32);
666 ft->dwHighDateTime = (uint32_t)(nt >> 32);
667 }
668
669 static uint32_t
670 convert_date_to_recurrence_minutes (const struct FILETIME *ft)
671 {
672 NTTIME minutes;
673
674 minutes = ft->dwHighDateTime;
675 minutes = minutes << 32;
676 minutes |= ft->dwLowDateTime;
677
678 minutes = minutes / (60 * cFileTimeUnitsPerSecond);
679
680 return (uint32_t)(minutes);
681 }
682
683 static time_t
684 convert_filetime_to_timet (const struct FILETIME *ft)
685 {
686 NTTIME time;
687
688 time = ft->dwHighDateTime;
689 time = time << 32;
690 time |= ft->dwLowDateTime;
691
692 return nt_time_to_unix (time);
693 }
694
695 static void
696 convert_timet_to_filetime (time_t t, struct FILETIME *ft)
697 {
698 NTTIME nt;
699
700 unix_to_nt_time (&nt, t);
701
702 ft->dwLowDateTime = (uint32_t)((nt << 32) >> 32);
703 ft->dwHighDateTime = (uint32_t)(nt >> 32);
704 }
705 #endif
706
707 static time_t
708 convert_recurrence_minutes_to_timet (uint32_t minutes)
709 {
710 NTTIME nt;
711
712 nt = (NTTIME) minutes * (60 * cFileTimeUnitsPerSecond);
713
714 return nt_time_to_unix (nt);
715 }
716
717 static uint32_t
718 convert_timet_to_recurrence_minutes (time_t t)
719 {
720 NTTIME minutes;
721
722 unix_to_nt_time (&minutes, t);
723
724 minutes = minutes / (60 * cFileTimeUnitsPerSecond);
725
726 return (uint32_t)(minutes);
727 }
728
729 static gboolean
730 check_calendar_type (guint16 type)
731 {
732 /* Calendar Type - We support Gregorian only. */
733 if (type == CAL_DEFAULT || type == CAL_GREGORIAN)
734 return TRUE;
735 else {
736 g_warning ("Calendar type = 0x%04X - Evolution does not handle such calendar types.", type);
737 return FALSE;
738 }
739 }
740
741 gboolean
742 e_mapi_cal_util_bin_to_rrule (const guint8 *lpb,
743 guint32 cb,
744 ECalComponent *comp,
745 GSList **extra_detached,
746 ICalTimezone *recur_zone)
747 {
748 ICalRecurrence *rt;
749 struct ema_AppointmentRecurrencePattern arp;
750 struct ema_RecurrencePattern *rp; /* Convenience pointer */
751 gboolean success = FALSE, check_calendar = FALSE;
752 gint i;
753 ptrdiff_t off = 0;
754 GSList *exdate_list = NULL;
755 GByteArray fake_ba;
756
757 if (e_mapi_debug_is_enabled ()) {
758 e_mapi_debug_print ("Converting binary to RRULE:");
759 e_mapi_debug_dump_bin (lpb, cb, 3);
760 e_mapi_debug_print ("\n");
761 }
762
763 fake_ba.data = (guint8 *) lpb;
764 fake_ba.len = cb;
765
766 rt = i_cal_recurrence_new ();
767
768 memset(&arp, 0, sizeof (struct ema_AppointmentRecurrencePattern));
769 if (! gba_to_arp (&fake_ba, &off, &arp))
770 goto cleanup;
771
772 rp = &arp.RecurrencePattern;
773
774 /* FREQUENCY */
775
776 if (rp->RecurFrequency == RecurFrequency_Daily) {
777 i_cal_recurrence_set_freq (rt, I_CAL_DAILY_RECURRENCE);
778
779 if (rp->PatternType == PatternType_Day) {
780 /* Daily every N days */
781
782 check_calendar = TRUE;
783
784 /* INTERVAL */
785 i_cal_recurrence_set_interval (rt, (short) (rp->Period / (24 * 60)));
786 } else if (rp->PatternType == PatternType_Week) {
787 /* Daily every weekday */
788
789 check_calendar = TRUE;
790
791 /* NOTE: Evolution does not handle daily-every-weekday
792 * any different from a weekly recurrence. */
793 i_cal_recurrence_set_freq (rt, I_CAL_WEEKLY_RECURRENCE);
794
795 /* INTERVAL */
796 i_cal_recurrence_set_interval (rt, (short) (rp->Period));
797 }
798 } else if (rp->RecurFrequency == RecurFrequency_Weekly) {
799 i_cal_recurrence_set_freq (rt, I_CAL_WEEKLY_RECURRENCE);
800
801 if (rp->PatternType == PatternType_Week) {
802 /* weekly every N weeks (for all events and non-regenerating tasks) */
803
804 check_calendar = TRUE;
805
806 /* INTERVAL */
807 i_cal_recurrence_set_interval (rt, (short) (rp->Period));
808 } else if (rp->PatternType == 0x0) {
809 /* weekly every N weeks (for all regenerating tasks) */
810
811 check_calendar = TRUE;
812
813 /* FIXME: we don't handle regenerating tasks */
814 g_warning ("Evolution does not handle recurring tasks.");
815 goto cleanup;
816 }
817
818 } else if (rp->RecurFrequency == RecurFrequency_Monthly) {
819 i_cal_recurrence_set_freq (rt, I_CAL_MONTHLY_RECURRENCE);
820
821 if (rp->PatternType == PatternType_Month ||
822 rp->PatternType == PatternType_MonthEnd) {
823 /* Monthly every N months on day D or last day. */
824
825 check_calendar = TRUE;
826
827 /* INTERVAL */
828 i_cal_recurrence_set_interval (rt, (short) (rp->Period));
829
830 /* MONTH_DAY */
831 if (rp->PatternType == PatternType_Month)
832 i_cal_recurrence_set_by_month_day (rt, 0, (short) (rp->PatternTypeSpecific));
833 else if (rp->PatternType == PatternType_MonthEnd)
834 i_cal_recurrence_set_by_month_day (rt, 0, (short) (-1));
835
836 } else if (rp->PatternType == PatternType_MonthNth) {
837 gboolean post_process = FALSE;
838 /* Monthly every N months on the Xth Y */
839
840 check_calendar = TRUE;
841
842 /* INTERVAL */
843 i_cal_recurrence_set_interval (rt, (short) (rp->Period));
844
845 /* BITMASK */
846 if (rp->PatternTypeSpecific == olSunday)
847 i_cal_recurrence_set_by_day (rt, 0, I_CAL_SUNDAY_WEEKDAY);
848 else if (rp->PatternTypeSpecific == olMonday)
849 i_cal_recurrence_set_by_day (rt, 0, I_CAL_MONDAY_WEEKDAY);
850 else if (rp->PatternTypeSpecific == olTuesday)
851 i_cal_recurrence_set_by_day (rt, 0, I_CAL_TUESDAY_WEEKDAY);
852 else if (rp->PatternTypeSpecific == olWednesday)
853 i_cal_recurrence_set_by_day (rt, 0, I_CAL_WEDNESDAY_WEEKDAY);
854 else if (rp->PatternTypeSpecific == olThursday)
855 i_cal_recurrence_set_by_day (rt, 0, I_CAL_THURSDAY_WEEKDAY);
856 else if (rp->PatternTypeSpecific == olFriday)
857 i_cal_recurrence_set_by_day (rt, 0, I_CAL_FRIDAY_WEEKDAY);
858 else if (rp->PatternTypeSpecific == olSaturday)
859 i_cal_recurrence_set_by_day (rt, 0, I_CAL_SATURDAY_WEEKDAY);
860 else {
861 post_process = TRUE;
862 }
863
864 /* RecurrenceN */
865 if (!post_process) {
866 i_cal_recurrence_set_by_set_pos (rt, 0, get_ical_pos (rp->N));
867 if (i_cal_recurrence_get_by_set_pos (rt, 0) == 0)
868 goto cleanup;
869 } else {
870 if (rp->PatternTypeSpecific == (olSunday | olMonday | olTuesday | olWednesday | olThursday | olFriday | olSaturday)) {
871 i_cal_recurrence_set_by_month_day (rt, 0, get_ical_pos (rp->N));
872 if (i_cal_recurrence_get_by_month_day (rt, 0) == 0)
873 goto cleanup;
874 } else {
875 /* FIXME: Can we/LibICAL support any other types here? Namely, weekday and weekend-day */
876 g_warning ("Encountered a recurrence type Evolution cannot handle. ");
877 goto cleanup;
878 }
879 }
880 }
881
882 } else if (rp->RecurFrequency == RecurFrequency_Yearly) {
883 i_cal_recurrence_set_freq (rt, I_CAL_YEARLY_RECURRENCE);
884
885 if (rp->PatternType == PatternType_Month) {
886 /* Yearly on day D of month M */
887
888 check_calendar = TRUE;
889
890 /* INTERVAL */
891 i_cal_recurrence_set_interval (rt, (short) (rp->Period / 12));
892
893 } else if (rp->PatternType == PatternType_MonthNth) {
894 /* Yearly on the Xth Y of month M */
895
896 g_warning ("Encountered a recurrence pattern Evolution cannot handle.");
897
898 /* TODO: Add support for this kinda recurrence in Evolution */
899 goto cleanup;
900 }
901 } else
902 goto cleanup;
903
904 /* Process by_day<->PatternTypeSpecific bitmasks for events that can
905 * occur on multiple days in a recurrence */
906 if ( (rp->RecurFrequency == RecurFrequency_Daily &&
907 rp->PatternType == PatternType_Week) ||
908 (rp->RecurFrequency == RecurFrequency_Weekly &&
909 rp->PatternType == PatternType_Week) ) {
910 i = 0;
911 if (rp->PatternTypeSpecific & olSunday)
912 i_cal_recurrence_set_by_day (rt, i++, I_CAL_SUNDAY_WEEKDAY);
913 if (rp->PatternTypeSpecific & olMonday)
914 i_cal_recurrence_set_by_day (rt, i++, I_CAL_MONDAY_WEEKDAY);
915 if (rp->PatternTypeSpecific & olTuesday)
916 i_cal_recurrence_set_by_day (rt, i++, I_CAL_TUESDAY_WEEKDAY);
917 if (rp->PatternTypeSpecific & olWednesday)
918 i_cal_recurrence_set_by_day (rt, i++, I_CAL_WEDNESDAY_WEEKDAY);
919 if (rp->PatternTypeSpecific & olThursday)
920 i_cal_recurrence_set_by_day (rt, i++, I_CAL_THURSDAY_WEEKDAY);
921 if (rp->PatternTypeSpecific & olFriday)
922 i_cal_recurrence_set_by_day (rt, i++, I_CAL_FRIDAY_WEEKDAY);
923 if (rp->PatternTypeSpecific & olSaturday)
924 i_cal_recurrence_set_by_day (rt, i++, I_CAL_SATURDAY_WEEKDAY);
925 }
926
927 /* Only some calendar types supported */
928 if (check_calendar && !check_calendar_type (rp->CalendarType))
929 goto cleanup;
930
931 /* End Type - followed by Occurence count */
932 if (rp->EndType == END_AFTER_N_OCCURRENCES) {
933 i_cal_recurrence_set_count (rt, rp->OccurrenceCount);
934 }
935
936 /* week_start */
937 i_cal_recurrence_set_week_start (rt, get_ical_weekstart (rp->FirstDOW));
938
939 /* number of exceptions */
940 if (rp->DeletedInstanceCount) {
941 for (i = 0; i < rp->DeletedInstanceCount; ++i) {
942 ICalTime *tt;
943 time_t ictime = convert_recurrence_minutes_to_timet (rp->DeletedInstanceDates[i]);
944
945 tt = i_cal_time_new_from_timet_with_zone (ictime, 1, NULL);
946
947 exdate_list = g_slist_append (exdate_list, e_cal_component_datetime_new_take (tt, g_strdup ("UTC")));
948 }
949 }
950
951 /* end date */
952 if (rp->EndType == END_AFTER_DATE) {
953 ICalTime *itt;
954
955 time_t ict = convert_recurrence_minutes_to_timet (rp->EndDate);
956
957 itt = i_cal_time_new_from_timet_with_zone (ict, 1, NULL);
958 i_cal_recurrence_set_until (rt, itt);
959 g_clear_object (&itt);
960 }
961
962 /* Set the recurrence */
963 {
964 GSList l;
965
966 l.data = rt;
967 l.next = NULL;
968
969 e_cal_component_set_rrules (comp, &l);
970 e_cal_component_set_exdates (comp, exdate_list);
971 }
972
973 g_slist_free_full (exdate_list, e_cal_component_datetime_free);
974 g_clear_object (&rt);
975
976 /* Modified exceptions */
977 if (arp.ExceptionCount && extra_detached) {
978 ECalComponent **detached = g_new0 (ECalComponent *,
979 arp.ExceptionCount);
980 ICalTime *tt;
981 ECalComponentDateTime *edt;
982 ECalComponentRange *rid;
983
984 e_cal_component_commit_sequence (comp);
985
986 for (i = 0; i < arp.ExceptionCount; i++) {
987 struct ema_ExceptionInfo *ei = &arp.ExceptionInfo[i];
988 struct ema_ExtendedException *ee = &arp.ExtendedException[i];
989 /* make a shallow clone of comp */
990 detached[i] = e_cal_component_clone (comp);
991
992 tt = i_cal_time_new_from_timet_with_zone (convert_recurrence_minutes_to_timet (ei->OriginalStartDate), 0, NULL);
993 rid = e_cal_component_range_new_take (E_CAL_COMPONENT_RANGE_SINGLE,
994 e_cal_component_datetime_new_take (tt, g_strdup (recur_zone ? i_cal_timezone_get_tzid (recur_zone) : "UTC")));
995 e_cal_component_set_recurid (detached[i], rid);
996 e_cal_component_range_free (rid);
997
998 tt = i_cal_time_new_from_timet_with_zone (convert_recurrence_minutes_to_timet (ei->StartDateTime), 0, NULL);
999 edt = e_cal_component_datetime_new_take (tt, g_strdup (recur_zone ? i_cal_timezone_get_tzid (recur_zone) : "UTC"));
1000 e_cal_component_set_dtstart (detached[i], edt);
1001 e_cal_component_datetime_free (edt);
1002
1003 tt = i_cal_time_new_from_timet_with_zone (convert_recurrence_minutes_to_timet (ei->EndDateTime), 0, NULL);
1004 edt = e_cal_component_datetime_new_take (tt, g_strdup (recur_zone ? i_cal_timezone_get_tzid (recur_zone) : "UTC"));
1005 e_cal_component_set_dtend (detached[i], edt);
1006 e_cal_component_datetime_free (edt);
1007
1008 e_cal_component_set_rdates (detached[i], NULL);
1009 e_cal_component_set_rrules (detached[i], NULL);
1010 e_cal_component_set_exdates (detached[i], NULL);
1011 e_cal_component_set_exrules (detached[i], NULL);
1012
1013 if (ee->WideCharSubject) {
1014 ECalComponentText *txt;
1015 gchar *str;
1016
1017 str = g_convert (ee->WideCharSubject,
1018 2 * ee->WideCharSubjectLength,
1019 "UTF-8", "UTF-16", NULL, NULL,
1020 NULL);
1021 txt = e_cal_component_text_new (str ? str : "", NULL);
1022 e_cal_component_set_summary (detached[i], txt);
1023 e_cal_component_text_free (txt);
1024 g_free (str);
1025 } else if (ei->Subject) {
1026 ECalComponentText *txt;
1027
1028 txt = e_cal_component_text_new (ei->Subject, NULL);
1029 e_cal_component_set_summary (detached[i], txt);
1030 e_cal_component_text_free (txt);
1031 }
1032
1033 /* FIXME: Handle MeetingType */
1034 /* FIXME: Handle ReminderDelta */
1035 /* FIXME: Handle Reminder */
1036
1037 if (ee->WideCharLocation) {
1038 gchar *str;
1039
1040 /* LocationLength */
1041 str = g_convert (ee->WideCharLocation,
1042 2 * ee->WideCharLocationLength,
1043 "UTF-8", "UTF-16", NULL, NULL,
1044 NULL);
1045 e_cal_component_set_location (detached[i], str);
1046 g_free (str);
1047 } else if (ei->Location) {
1048 e_cal_component_set_location (detached[i], ei->Location);
1049 }
1050
1051 /* FIXME: Handle BusyStatus? */
1052 /* FIXME: Handle Attachment? */
1053 /* FIXME: Handle SubType? */
1054 /* FIXME: Handle AppointmentColor? */
1055 /* FIXME: do we do anything with ChangeHighlight? */
1056
1057 *extra_detached = g_slist_append (*extra_detached,
1058 detached[i]);
1059 }
1060 g_free (detached);
1061 }
1062
1063 success = TRUE;
1064 cleanup:
1065 free_arp_contents (&arp);
1066 return success;
1067 }
1068
1069 static guint32
1070 compute_startdate (ECalComponent *comp)
1071 {
1072 ECalComponentDateTime *dtstart;
1073 ICalTime *itt;
1074 guint32 flag32;
1075
1076 dtstart = e_cal_component_get_dtstart (comp);
1077 if (!dtstart)
1078 return 0;
1079
1080 itt = e_cal_component_datetime_get_value (dtstart);
1081 i_cal_time_set_time (itt, 0, 0, 0);
1082
1083 flag32 = convert_timet_to_recurrence_minutes (i_cal_time_as_timet_with_zone (itt, NULL));
1084
1085 e_cal_component_datetime_free (dtstart);
1086
1087 return flag32;
1088 }
1089
1090 static guint32
1091 compute_rdaily_firstdatetime (ECalComponent *comp,
1092 guint32 period)
1093 {
1094 return (compute_startdate (comp) % period);
1095 }
1096
1097 static guint32
1098 compute_rweekly_firstdatetime (ECalComponent *comp,
1099 ICalRecurrenceWeekday week_start,
1100 guint32 period)
1101 {
1102 ECalComponentDateTime *dtstart;
1103 ICalTime *itt;
1104 guint32 flag32;
1105 gint cur_weekday = 0, weekstart_weekday = 0, diff = 0;
1106 time_t t;
1107
1108 dtstart = e_cal_component_get_dtstart (comp);
1109 if (!dtstart)
1110 return 0;
1111
1112 itt = e_cal_component_datetime_get_value (dtstart);
1113 i_cal_time_set_time (itt, 0, 0, 0);
1114 cur_weekday = i_cal_time_day_of_week (itt);
1115 t = i_cal_time_as_timet_with_zone (itt, NULL);
1116 e_cal_component_datetime_free (dtstart);
1117
1118 switch (week_start) {
1119 case I_CAL_SUNDAY_WEEKDAY:
1120 weekstart_weekday = 1; break;
1121 case I_CAL_MONDAY_WEEKDAY:
1122 weekstart_weekday = 2; break;
1123 case I_CAL_TUESDAY_WEEKDAY:
1124 weekstart_weekday = 3; break;
1125 case I_CAL_WEDNESDAY_WEEKDAY:
1126 weekstart_weekday = 4; break;
1127 case I_CAL_THURSDAY_WEEKDAY:
1128 weekstart_weekday = 5; break;
1129 case I_CAL_FRIDAY_WEEKDAY:
1130 weekstart_weekday = 6; break;
1131 case I_CAL_SATURDAY_WEEKDAY:
1132 weekstart_weekday = 7; break;
1133 default:
1134 weekstart_weekday = 1; break;
1135 };
1136
1137 diff = cur_weekday - weekstart_weekday;
1138
1139 if (diff == 0);
1140 else if (diff > 0)
1141 t -= (diff * 24 * 60 * 60);
1142 else if (diff < 0)
1143 t -= ((diff + 7) * 24 * 60 * 60);
1144
1145 flag32 = convert_timet_to_recurrence_minutes (t);
1146
1147 return (flag32 % period);
1148 }
1149
1150 /* The most fucked up algorithm ever conceived by (..you know who..) */
1151 static guint32
1152 compute_rmonthly_firstdatetime (ECalComponent *comp,
1153 guint32 period)
1154 {
1155 const guint8 dinm[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
1156 ECalComponentDateTime *dtstart;
1157 ICalTime *itt;
1158 guint32 flag32, monthindex, i;
1159
1160 dtstart = e_cal_component_get_dtstart (comp);
1161 if (!dtstart)
1162 return 0;
1163 itt = e_cal_component_datetime_get_value (dtstart);
1164 monthindex = (guint32)((((guint64)(12) * (i_cal_time_get_year (itt)- 1601)) + (i_cal_time_get_month (itt) - 1)) % period);
1165 e_cal_component_datetime_free (dtstart);
1166
1167 for (flag32 = 0, i = 0; i < monthindex; ++i)
1168 flag32 += dinm[(i % 12) + 1] * 24 * 60;
1169
1170 return flag32;
1171 }
1172
1173 static guint32
1174 calculate_no_of_occurrences (ECalComponent *comp,
1175 ICalRecurrence *rt)
1176 {
1177 ECalComponentDateTime *dtstart;
1178 ICalRecurIterator *iter;
1179 ICalTime *next, *prev = NULL;
1180 guint32 count = 1;
1181
1182 dtstart = e_cal_component_get_dtstart (comp);
1183 if (!dtstart)
1184 return 1;
1185
1186 iter = i_cal_recur_iterator_new (rt, e_cal_component_datetime_get_value (dtstart));
1187 if (iter) {
1188 for (next = i_cal_recur_iterator_next (iter);
1189 next && !i_cal_time_is_null_time (next);
1190 g_object_unref (next), next = i_cal_recur_iterator_next (iter)) {
1191 if (prev && !i_cal_time_is_null_time (prev) &&
1192 i_cal_time_compare (next, prev) == 0)
1193 break;
1194
1195 g_clear_object (&prev);
1196 prev = i_cal_time_clone (next);
1197
1198 count++;
1199 }
1200
1201 g_clear_object (&iter);
1202 g_clear_object (&prev);
1203 g_clear_object (&next);
1204 }
1205
1206 e_cal_component_datetime_free (dtstart);
1207
1208 return count;
1209 }
1210
1211 static gint
1212 compare_guint32 (gconstpointer a,
1213 gconstpointer b,
1214 gpointer user_data)
1215 {
1216 return (*((guint32 *) a) - *((guint32 *) b));
1217 }
1218
1219 gboolean
1220 e_mapi_cal_util_rrule_to_bin (ECalComponent *comp,
1221 struct SBinary_short *bin,
1222 TALLOC_CTX *mem_ctx)
1223 {
1224 ICalRecurrence *rt;
1225 ICalTime *until;
1226 gint i;
1227 GSList *rrule_list, *exdate_list;
1228 GByteArray *ba = NULL;
1229 struct ema_AppointmentRecurrencePattern arp;
1230 struct ema_RecurrencePattern *rp; /* Convenience ptr */
1231
1232 g_return_val_if_fail (comp != NULL, FALSE);
1233 g_return_val_if_fail (bin != NULL, FALSE);
1234 g_return_val_if_fail (mem_ctx != NULL, FALSE);
1235
1236 if (!e_cal_component_has_recurrences (comp))
1237 return FALSE;
1238
1239 rrule_list = e_cal_component_get_rrules (comp);
1240 if (g_slist_length (rrule_list) != 1) {
1241 g_slist_free_full (rrule_list, g_object_unref);
1242 return FALSE;
1243 }
1244
1245 exdate_list = e_cal_component_get_exdates (comp);
1246
1247 rt = rrule_list->data;
1248
1249 ba = g_byte_array_new ();
1250 memset (&arp, 0, sizeof (struct ema_AppointmentRecurrencePattern));
1251 rp = &arp.RecurrencePattern;
1252
1253 /* Reader Version */
1254 rp->ReaderVersion = READER_VERSION;
1255 rp->WriterVersion = WRITER_VERSION;
1256
1257 /* Calendar Type */
1258 rp->CalendarType = CAL_DEFAULT;
1259
1260 if (i_cal_recurrence_get_freq (rt) == I_CAL_DAILY_RECURRENCE) {
1261 rp->RecurFrequency = RecurFrequency_Daily;
1262
1263 /* Pattern Type - it would be PatternType_Day since we have
1264 * only "Daily every N days". The other type would be
1265 * parsed as a weekly recurrence. */
1266 rp->PatternType = PatternType_Day;
1267
1268 /* FirstDateTime */
1269 rp->FirstDateTime = compute_rdaily_firstdatetime (comp, (i_cal_recurrence_get_interval (rt) * (60 * 24)));
1270
1271 /* INTERVAL */
1272 rp->Period = (i_cal_recurrence_get_interval (rt) * (60 * 24));
1273
1274 /* No PatternTypeSpecific for PatternType_Day */
1275
1276 } else if (i_cal_recurrence_get_freq (rt) == I_CAL_WEEKLY_RECURRENCE) {
1277 rp->RecurFrequency = RecurFrequency_Weekly;
1278
1279 /* Pattern Type - it would be PatternType_Week since we don't
1280 * support any other type. */
1281 rp->PatternType = PatternType_Week;
1282
1283 /* FirstDateTime */
1284 rp->FirstDateTime = compute_rweekly_firstdatetime (comp, i_cal_recurrence_get_week_start (rt), (i_cal_recurrence_get_interval (rt) * (60 * 24 * 7)));
1285
1286 /* INTERVAL */
1287 rp->Period = i_cal_recurrence_get_interval (rt);
1288
1289 /* BITMASK */
1290 for (i = 0; i < I_CAL_BY_DAY_SIZE; ++i) {
1291 if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_SUNDAY_WEEKDAY)
1292 rp->PatternTypeSpecific |= olSunday;
1293 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_MONDAY_WEEKDAY)
1294 rp->PatternTypeSpecific |= olMonday;
1295 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_TUESDAY_WEEKDAY)
1296 rp->PatternTypeSpecific |= olTuesday;
1297 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_WEDNESDAY_WEEKDAY)
1298 rp->PatternTypeSpecific |= olWednesday;
1299 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_THURSDAY_WEEKDAY)
1300 rp->PatternTypeSpecific |= olThursday;
1301 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_FRIDAY_WEEKDAY)
1302 rp->PatternTypeSpecific |= olFriday;
1303 else if (i_cal_recurrence_get_by_day (rt, i) == I_CAL_SATURDAY_WEEKDAY)
1304 rp->PatternTypeSpecific |= olSaturday;
1305 else
1306 break;
1307 }
1308
1309 } else if (i_cal_recurrence_get_freq (rt) == I_CAL_MONTHLY_RECURRENCE) {
1310 guint16 pattern = 0x0; guint32 mask = 0x0, flag = 0x0;
1311
1312 rp->RecurFrequency = RecurFrequency_Monthly;
1313
1314 if (i_cal_recurrence_get_by_month_day (rt, 0) >= 1 && i_cal_recurrence_get_by_month_day (rt, 0) <= 31) {
1315 pattern = PatternType_Month;
1316 flag = i_cal_recurrence_get_by_month_day (rt, 0);
1317 } else if (i_cal_recurrence_get_by_month_day (rt, 0) == -1) {
1318 pattern = PatternType_MonthNth;
1319 mask = (olSunday | olMonday | olTuesday | olWednesday | olThursday | olFriday | olSaturday);
1320 flag = RecurrenceN_Last;
1321 } else if (i_cal_recurrence_get_by_day (rt, 0) >= I_CAL_SUNDAY_WEEKDAY && i_cal_recurrence_get_by_day (rt, 0) <= I_CAL_SATURDAY_WEEKDAY) {
1322 pattern = PatternType_MonthNth;
1323 mask = get_mapi_day (i_cal_recurrence_get_by_day (rt, 0));
1324 flag = get_mapi_pos (i_cal_recurrence_get_by_set_pos (rt, 0));
1325 }
1326
1327 rp->PatternType = pattern;
1328
1329 /* FirstDateTime */
1330 rp->FirstDateTime = compute_rmonthly_firstdatetime (comp, i_cal_recurrence_get_interval (rt));
1331
1332 /* INTERVAL */
1333 rp->Period = i_cal_recurrence_get_interval (rt);
1334
1335 if (pattern == PatternType_Month) {
1336 rp->N = flag;
1337 } else if (pattern == PatternType_MonthNth) {
1338 rp->PatternTypeSpecific = mask;
1339 rp->N = flag;
1340 }
1341
1342 /* Warn for different cases where we might be sending
1343 * untranslatable values */
1344 if ( (pattern == PatternType_Month && !(flag)) ||
1345 (pattern == PatternType_MonthNth && !(flag && mask)) ||
1346 (pattern != PatternType_Month && pattern != PatternType_MonthNth) ) {
1347 g_warning ("Possibly setting incorrect values in the stream. ");
1348 }
1349
1350 } else if (i_cal_recurrence_get_freq (rt) == I_CAL_YEARLY_RECURRENCE) {
1351 rp->RecurFrequency = RecurFrequency_Yearly;
1352
1353 /* Pattern Type - it would be PatternType_Month since we don't
1354 * support any other type. */
1355 rp->PatternType = PatternType_Month;
1356
1357 /* FirstDateTime - uses the same function as monthly
1358 * recurrence */
1359 rp->FirstDateTime = compute_rmonthly_firstdatetime (comp, 0xC);
1360
1361 /* INTERVAL - should be 12 for yearly recurrence */
1362 rp->Period = 0xC;
1363
1364 /* MONTH_DAY */
1365 {
1366 ECalComponentDateTime *dtstart;
1367 dtstart = e_cal_component_get_dtstart (comp);
1368 rp->PatternTypeSpecific = (dtstart && e_cal_component_datetime_get_value (dtstart)) ?
1369 i_cal_time_get_day (e_cal_component_datetime_get_value (dtstart)) : 0;
1370 e_cal_component_datetime_free (dtstart);
1371 }
1372 }
1373
1374 until = i_cal_recurrence_get_until (rt);
1375
1376 /* End Type followed by Occurence count */
1377 if (until && !i_cal_time_is_null_time (until)) {
1378 rp->EndType = END_AFTER_DATE;
1379 rp->OccurrenceCount = calculate_no_of_occurrences (comp, rt);
1380 } else if (i_cal_recurrence_get_count (rt)) {
1381 rp->EndType = END_AFTER_N_OCCURRENCES;
1382 rp->OccurrenceCount = i_cal_recurrence_get_count (rt);
1383 } else {
1384 rp->EndType = END_NEVER_END;
1385 }
1386
1387 g_clear_object (&until);
1388
1389 /* FirstDOW */
1390 rp->FirstDOW = get_mapi_weekstart (i_cal_recurrence_get_week_start (rt));
1391
1392 /* DeletedInstanceDates */
1393 rp->DeletedInstanceCount = g_slist_length (exdate_list);
1394 if (rp->DeletedInstanceCount) {
1395 GSList *l;
1396 ECalComponentDateTime *dt;
1397 rp->DeletedInstanceDates = g_new0(guint32,
1398 rp->DeletedInstanceCount);
1399 /* FIXME: This should include modified dates */
1400 for (i = 0, l = exdate_list; l; ++i, l = l->next) {
1401 ICalTime *itt;
1402
1403 dt = l->data;
1404
1405 itt = e_cal_component_datetime_get_value (dt);
1406 if (!itt)
1407 continue;
1408
1409 i_cal_time_set_time (itt, 0, 0, 0);
1410
1411 rp->DeletedInstanceDates[i] = convert_timet_to_recurrence_minutes (i_cal_time_as_timet_with_zone (itt, NULL));
1412 }
1413
1414 g_qsort_with_data (rp->DeletedInstanceDates,
1415 rp->DeletedInstanceCount,
1416 sizeof (guint32), compare_guint32, NULL);
1417 }
1418
1419 /* FIXME: Add support for modified instances
1420 * (currently we send valid data saying no modified instances) */
1421
1422 /* StartDate */
1423 rp->StartDate = compute_startdate (comp);
1424
1425 /* EndDate */
1426 {
1427 if (rp->EndType == END_NEVER_END)
1428 /* FIXME: named prop here? */
1429 rp->EndDate = 0x5AE980DF;
1430 else if (rp->EndType == END_AFTER_N_OCCURRENCES) {
1431 ECalComponentDateTime *dtstart;
1432 ICalTime *itt;
1433 gchar *rrule_str = i_cal_recurrence_to_string (rt);
1434 GArray *array;
1435
1436 dtstart = e_cal_component_get_dtstart (comp);
1437 itt = e_cal_component_datetime_get_value (dtstart);
1438 i_cal_time_set_time (itt, 0, 0, 0);
1439
1440 array = i_cal_recur_expand_recurrence (rrule_str, i_cal_time_as_timet_with_zone (itt, NULL), i_cal_recurrence_get_count (rt));
1441 if (array) {
1442 rp->EndDate = convert_timet_to_recurrence_minutes (g_array_index (array, time_t, i_cal_recurrence_get_count (rt) - 1));
1443 g_array_unref (array);
1444 } else {
1445 g_warn_if_reached ();
1446 }
1447
1448 g_free (rrule_str);
1449 e_cal_component_datetime_free (dtstart);
1450 } else if (rp->EndType == END_AFTER_DATE) {
1451 until = i_cal_recurrence_get_until (rt);
1452 i_cal_time_set_time (until, 0, 0, 0);
1453
1454 rp->EndDate = convert_timet_to_recurrence_minutes (i_cal_time_as_timet_with_zone (until, NULL));
1455 g_clear_object (&until);
1456 }
1457 }
1458
1459 /* Reader Version 2 */
1460 arp.ReaderVersion2 = READER_VERSION2;
1461 /* Writer Version 2 */
1462 arp.WriterVersion2 = WRITER_VERSION2;
1463
1464 /* StartTimeOffset */
1465 {
1466 ECalComponentDateTime *dtstart;
1467 ICalTime *itt;
1468
1469 dtstart = e_cal_component_get_dtstart (comp);
1470 itt = e_cal_component_datetime_get_value (dtstart);
1471
1472 arp.StartTimeOffset = (i_cal_time_get_hour (itt) * 60) + i_cal_time_get_minute (itt);
1473 e_cal_component_datetime_free (dtstart);
1474 }
1475
1476 /* EndTimeOffset */
1477 {
1478 ECalComponentDateTime *dtend;
1479 ICalTime *itt;
1480
1481 dtend = e_cal_component_get_dtend (comp);
1482 itt = e_cal_component_datetime_get_value (dtend);
1483 arp.EndTimeOffset = (i_cal_time_get_hour (itt) * 60) + i_cal_time_get_minute (itt);
1484 e_cal_component_datetime_free (dtend);
1485 }
1486
1487 /* FIXME: Add ExceptionInfo here */
1488 /* FIXME: Add the ExtendedExceptionInfo here */
1489
1490 /* Reserved Block 2 Size */
1491 arp_to_gba (&arp, ba);
1492
1493 free_arp_contents (&arp);
1494 g_slist_free_full (exdate_list, e_cal_component_datetime_free);
1495 g_slist_free_full (rrule_list, g_object_unref);
1496
1497 /*g_print ("\n== ICAL to MAPI == The recurrence blob data is as follows:\n");
1498 for (i = 0; i < ba->len; ++i)
1499 g_print ("0x%02X ", ba->data[i]);
1500 g_print("\n== End of stream ==\n");*/
1501
1502 bin->cb = ba->len;
1503 bin->lpb = talloc_memdup (mem_ctx, ba->data, ba->len);
1504
1505 g_byte_array_free (ba, TRUE);
1506
1507 return TRUE;
1508 }