"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 }