dtcm: Resolve CID 87822
[oweals/cde.git] / cde / programs / dtcm / server / v4ops.c
1 /*
2  * CDE - Common Desktop Environment
3  *
4  * Copyright (c) 1993-2012, The Open Group. All rights reserved.
5  *
6  * These libraries and programs are free software; you can
7  * redistribute them and/or modify them under the terms of the GNU
8  * Lesser General Public License as published by the Free Software
9  * Foundation; either version 2 of the License, or (at your option)
10  * any later version.
11  *
12  * These libraries and programs are distributed in the hope that
13  * they will be useful, but WITHOUT ANY WARRANTY; without even the
14  * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
15  * PURPOSE. See the GNU Lesser General Public License for more
16  * details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with these librararies and programs; if not, write
20  * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
21  * Floor, Boston, MA 02110-1301 USA
22  */
23 /* $XConsortium: v4ops.c /main/5 1996/10/02 17:21:09 drk $ */
24 /*
25  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
26  *  (c) Copyright 1993, 1994 International Business Machines Corp.
27  *  (c) Copyright 1993, 1994 Novell, Inc.
28  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
29  */
30
31 #include <EUSCompat.h>
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <sys/stat.h>
35 #include <errno.h>
36 #include <string.h>
37 #include <pwd.h>
38 #include <time.h>
39 #if defined(CSRG_BASED)
40 #if defined(__NetBSD__)
41 #include <limits.h>
42 #else
43 #include <sys/limits.h>
44 #endif
45 #define MAXINT INT_MAX
46 #else
47 #include <values.h>
48 #endif
49 #ifdef SunOS
50 #include <sys/systeminfo.h>
51 #endif
52 #include "v4ops.h"
53 #include "rtable4.h"
54 #include "cmscalendar.h"
55 #include "tree.h"
56 #include "list.h"
57 #include "log.h"
58 #include "appt4.h"              /* Internal appointment data structure */
59 #include "reminder.h"
60 #include "access.h"
61 #include "laccess.h"
62 #include "lookup.h"
63 #include "cmsconvert.h"
64 #include "cmsdata.h"
65 #include "repeat.h"
66 #include "misc.h"
67 #include "utility.h"
68
69 extern  int     debug;
70 extern  char    *pgname;
71
72 /*****************************************************************************
73  * forward declaration of static functions used within the file
74  *****************************************************************************/
75 static Exception_4 append_exception_list(Appt_4 *p_appt, int ordinal);
76 static void trunc_exception_list(Appt_4 *p_appt, int ordinal);
77 static int num_exception(Appt_4 *p_appt);
78
79 /*****************************************************************************
80  * extern functions used in the library
81  *****************************************************************************/
82
83 /*
84  * Insert one appointment into the single appointment tree structure
85  * or the repeat appointment list structure.
86  * The passed in appointment is stored in the structure.
87  */
88 extern CSA_return_code
89 _DtCmsInsertAppt(_DtCmsCalendar *cal, Appt_4 *appt4)
90 {
91         CSA_return_code stat;
92         Rb_Status       rb_stat;
93         time_t          current_time;
94         Attr_4          p_attr;
95
96         if (cal == NULL || appt4 == NULL)
97                 return (CSA_E_INVALID_PARAMETER);
98
99         /* assign key if this is a new appointment */
100         _DtCmsGenerateKey(cal, &(appt4->appt_id.key));
101
102         if (debug) {
103                 fprintf(stderr, "Insert appt4: (%ld)%s\n",
104                         appt4->appt_id.key, ctime(&(appt4->appt_id.tick)));
105         }
106
107         /* Add the appointment into the data structure */
108         if (appt4->period.period == single_4)
109                 rb_stat = rb_insert (cal->tree, (caddr_t)appt4,
110                                 (caddr_t)&(appt4->appt_id));
111         else
112                 rb_stat = hc_insert (REPT_LIST(cal), (caddr_t)appt4,
113                                 (caddr_t)&(appt4->appt_id), NULL, NULL);
114
115         if (rb_stat == rb_ok) {
116                 /* Add the qualified reminder attrs to the reminder queue */
117                 current_time = time(0);
118                 p_attr = appt4->attr;
119                 while (p_attr != NULL)
120                 {
121                         time_t  tick;
122                         Rm_que  *p_reminder;
123
124                         tick = appt4->appt_id.tick - atol(p_attr->value);
125                         p_reminder = build_reminder (current_time, appt4,
126                                                         p_attr, tick, 1);
127                         if (p_reminder != NULL)
128                                 _DtCmsAddReminderV4(&cal->rm_queue, p_reminder);
129                         p_attr = p_attr->next;
130                 }
131         }
132
133         return (_DtCmsRbToCsaStat(rb_stat));
134 }
135
136 /*
137  * If p_auth is null, the initiator is the owner of the calendar.  Permission
138  * to delete any appointment is always granted.  If p_auth is not null, we need
139  * to check if it matches the author of the deleting appointment.  Only the
140  * author can delete his/her appointment.
141  */
142 extern CSA_return_code
143 _DtCmsDeleteAppt(
144         _DtCmsCalendar  *cal,
145         char            *user,
146         uint            access,
147         Id_4            *p_key,
148         Appt_4          **appt_r)
149 {
150         Appt_4          *p_appt;
151         List_node       *p_lnode;
152         Tree_node       *p_node;
153
154         /*
155          * Before we delete an event from the single appointment tree, we
156          * need to check if the initiator is the author of the appointment.
157          */
158         if ((user != NULL) && (p_appt = (Appt_4 *)rb_lookup(APPT_TREE(cal),
159             (caddr_t)p_key)) != NULL) {
160                 if (!(access & (access_delete_4|CSA_OWNER_RIGHTS)) &&
161                     !_DtCmIsSameUser(user, p_appt->author))
162                         return (CSA_E_NO_AUTHORITY);
163         }
164
165         if ((p_node = rb_delete (APPT_TREE(cal), (caddr_t)p_key)) != NULL) {
166                 p_appt = (Appt_4*)p_node->data;
167                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE);
168                 free (p_node);
169                 if (debug)
170                 {
171                         fprintf (stderr, "Deleted (%ld)%s\n",
172                                 APPT_KEY(p_appt),
173                                 ctime(&(p_appt->appt_id.tick)));
174                 }
175                 if (appt_r != NULL)
176                         *appt_r = p_appt;
177                 else
178                         _DtCm_free_appt4(p_appt);
179                 return (CSA_SUCCESS);
180         }
181
182         /* Attempt to delete the event from the repeating appointment list */
183         p_lnode = (List_node *)hc_lookup_node(REPT_LIST(cal),
184                         (caddr_t)p_key);
185
186         if (p_lnode != NULL) {
187                 p_appt = (Appt_4 *)p_lnode->data;
188
189                 if (user != NULL &&
190                     !(access & (access_delete_4|CSA_OWNER_RIGHTS)) &&
191                     !_DtCmIsSameUser(user, p_appt->author))
192                         return (CSA_E_NO_AUTHORITY);
193
194                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0, B_FALSE);
195                 (void) hc_delete_node (REPT_LIST(cal), p_lnode);
196                 free (p_lnode);
197                 if (debug)
198                 {
199                         fprintf (stderr, "Deleted (%ld)%s\n",
200                                 APPT_KEY(p_appt),
201                                 ctime(&(p_appt->appt_id.tick)));
202                 }
203                 if (appt_r != NULL)
204                         *appt_r = p_appt;
205                 else
206                         _DtCm_free_appt4(p_appt);
207                 return (CSA_SUCCESS);
208         }
209         return (CSA_E_USER_NOT_FOUND | CSA_E_INVALID_ENTRY_HANDLE);
210 }
211
212 extern CSA_return_code
213 _DtCmsDeleteApptAndLog(
214         _DtCmsCalendar  *cal,
215         char            *user,
216         uint            access,
217         Id_4            *key,
218         Appt_4          **oldappt)
219 {
220         CSA_return_code stat;
221         Appt_4 *appt;
222
223         if ((stat = _DtCmsDeleteAppt(cal, user, access, key, &appt))
224             == CSA_SUCCESS) {
225
226                 /* Transact the log */
227                 if ((stat = v4_transact_log(cal->calendar, appt,
228                     _DtCmsLogRemove)) != CSA_SUCCESS) {
229
230                         (void)_DtCmsInsertAppt(cal, appt);
231
232                 } else if (oldappt) {
233                         *oldappt = appt;
234                 } else {
235                         _DtCm_free_appt4(appt);
236                 }
237         }
238
239         return (stat);
240 }
241
242 extern CSA_return_code
243 _DtCmsDeleteApptInstancesAndLog(
244         _DtCmsCalendar  *cal,
245         char            *source,
246         uint            access,
247         Id_4            *key,
248         Options_4       option,
249         int             *remain,
250         Appt_4          **oldappt)
251 {
252         List_node       *p_lnode;
253         CSA_return_code stat = CSA_SUCCESS;
254         Appt_4          *p_appt, *oldcopy;
255         int             ordinal;
256         int             f, file_size, ntimes, ninstance, status = 0;
257         struct stat     info;
258
259         p_lnode = (List_node *)hc_lookup_node (REPT_LIST(cal), (caddr_t)key);
260         if (p_lnode == NULL)
261                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
262
263         p_appt = (Appt_4*)p_lnode->data;
264         if (!(access & (access_delete_4 | CSA_OWNER_RIGHTS)) &&
265             !_DtCmIsSameUser(source, p_appt->author))
266                 return (CSA_E_NO_AUTHORITY);
267
268         if ((ordinal = _DtCms_in_repeater (key, p_appt, B_TRUE)) == 0)
269                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
270
271         if (debug)
272                 fprintf(stderr,"Delete instance: Ordinal=%d\n",ordinal);
273
274         /*
275          * save file size in case the first log transaction
276          * succeeds but the second log transaction fails.
277          */
278         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
279             != CSA_SUCCESS)
280                 return (stat);
281
282         /* remove from log */
283         if ((stat = v4_transact_log(cal->calendar, p_appt, _DtCmsLogRemove))
284             != 0) {
285                 return (stat);
286         }
287         ninstance = _DtCms_get_ninstance_v4(p_appt);
288
289         /* calculate the ntimes value for the part
290          * of the sequence that start from the ordinal to the end
291          */
292         if (remain != NULL)
293                 *remain = _DtCms_get_new_ntimes_v4(p_appt->period, key->tick,
294                                 (ninstance - ordinal + 1));
295
296         /*
297          * make a copy of the original appointment,
298          * in case we need a rollback
299          */
300         if ((oldcopy = _DtCm_copy_one_appt4(p_appt)) == NULL) {
301                 stat = CSA_E_INSUFFICIENT_MEMORY;
302                 goto delete_cleanup;
303         }
304
305         /* remove from memory */
306         if (option == do_one_4) {
307                 if (!append_exception_list (p_appt, ordinal)) {
308                         stat = CSA_E_INSUFFICIENT_MEMORY;
309                         goto delete_cleanup;
310                 }
311         } else {
312                 ninstance = ordinal - 1;
313                 if (ninstance == 1) {
314                         /* convert to one-time event */
315                         p_appt->period.period = single_4;
316                         p_appt->period.nth = 0;
317                         p_appt->period.enddate = 0;
318                         p_appt->ntimes = 0;
319                         if (p_appt->exception) {
320                                 _DtCm_free_excpt4(p_appt->exception);
321                                 p_appt->exception = NULL;
322                         }
323                         stat = _DtCmsRbToCsaStat(rb_insert(cal->tree,
324                                 (caddr_t)p_appt, (caddr_t)&(p_appt->appt_id)));
325                 } else {
326                         p_appt->ntimes = _DtCms_get_new_ntimes_v4(
327                                         p_appt->period, p_appt->appt_id.tick,
328                                         ninstance);
329
330                         /* update enddate just for M-F, MWF, TTh
331                          * and weekdaycombo
332                          */
333                         switch (p_appt->period.period) {
334                         case monThruFri_4:
335                         case monWedFri_4:
336                         case tueThur_4:
337                         case daysOfWeek_4:
338                                 p_appt->period.enddate = _DtCms_prev_tick_v4(
339                                                                 key->tick,
340                                                                 p_appt->period);
341                         }
342                         trunc_exception_list(p_appt, ordinal);
343                 }
344         }
345
346         /* The last one from the series has been deleted, no more left. */
347         if (ninstance == num_exception (p_appt))
348                 ordinal = 0;
349
350         if (ordinal == 0) {
351                 /* Obsolete the reminders which match the ordinal */
352                 _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, ordinal,
353                         (option == do_one_4 ? B_FALSE : B_TRUE));
354                 _DtCm_free_appt4(p_appt);
355                 hc_delete_node (REPT_LIST(cal), p_lnode);
356                 free (p_lnode);
357         } else {
358                 /* Write out the series with new exception list. */
359                 if (stat || (stat = v4_transact_log(cal->calendar, p_appt,
360                     _DtCmsLogAdd)) != CSA_SUCCESS) {
361                         /* reverse memory update */
362                         p_appt->ntimes = oldcopy->ntimes;
363                         p_appt->period = oldcopy->period;
364                         _DtCm_free_excpt4(p_appt->exception);
365                         p_appt->exception = oldcopy->exception;
366                         oldcopy->exception = NULL;
367                         goto delete_cleanup;
368                 }
369
370                 if (ninstance == 1) {
371                         time_t  current_time;
372                         Attr_4  p_attr;
373
374                         _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt, 0,
375                                 (option == do_one_4 ? B_FALSE : B_TRUE));
376                         hc_delete_node(REPT_LIST(cal), p_lnode);
377                         free(p_lnode);
378
379                         /* Add the qualified reminder attrs to
380                          * the reminder queue
381                          */
382                         current_time = time(0);
383                         p_attr = p_appt->attr;
384                         while (p_attr != NULL) {
385                                 time_t  tick;
386                                 Rm_que  *p_reminder;
387
388                                 tick = p_appt->appt_id.tick-atol(p_attr->value);
389                                 p_reminder = build_reminder(current_time,
390                                                 p_appt, p_attr, tick, 1);
391                                 if (p_reminder != NULL)
392                                         _DtCmsAddReminderV4(&cal->rm_queue,
393                                                 p_reminder);
394                                 p_attr = p_attr->next;
395                         }
396
397                 } else {
398                         _DtCmsObsoleteReminderV4(&cal->rm_queue, p_appt,
399                                 ordinal,
400                                 (option == do_one_4 ? B_FALSE : B_TRUE));
401                 }
402         }
403
404         if (oldappt)
405                 *oldappt = oldcopy;
406         else
407                 _DtCm_free_appt4(oldcopy);
408
409         return (CSA_SUCCESS);
410
411 delete_cleanup:
412         /* rollback log file */
413         _DtCmsTruncateFile(cal->calendar, file_size);
414
415         if (oldcopy)
416                 _DtCm_free_appt4(oldcopy);
417
418         return (stat);
419 }
420
421 extern CSA_return_code
422 _DtCmsChangeAll(
423         _DtCmsCalendar  *cal,
424         char            *source,
425         uint            access,
426         Id_4            *p_key,
427         Appt_4          *newa,
428         Appt_4          **oldappt)
429 {
430         CSA_return_code stat = CSA_SUCCESS;
431         Appt_4  *olda, *newcopy = NULL;
432         int     ordinal;
433         int     file_size;
434
435         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
436             != CSA_SUCCESS)
437                 return (stat);
438
439         /*
440          * first, remove the old appointment from internal data structure
441          * and the callog file
442          */
443         if ((stat = _DtCmsDeleteApptAndLog(cal, source, access, p_key, &olda))
444             != CSA_SUCCESS)
445                 return (stat);
446
447         if (is_repeater(olda) && is_repeater(newa)) {
448                 if (_DtCmsBeginOfDay(APPT_TICK(newa)) ==
449                     _DtCmsBeginOfDay(p_key->tick) &&
450                     olda->period.period == newa->period.period)
451                 {
452                         /* keep the start day of the original
453                          * appointment if the date of the
454                          * key matches that of the new
455                          * appointment and the interval
456                          * is not changed
457                          */
458                         APPT_TICK(newa) -= _DtCmsBeginOfDay(APPT_TICK(newa)) -
459                                         _DtCmsBeginOfDay(APPT_TICK(olda));
460                 } else {
461                         /* otherwise, calculate new parent */
462                         if ((ordinal = _DtCms_in_repeater(p_key, olda, B_TRUE))
463                             == 0) {
464                                 stat = CSA_X_DT_E_ENTRY_NOT_FOUND;
465                                 goto change_all_cleanup;
466                         } else {
467                                 APPT_TICK(newa) = _DtCms_first_tick_v4(
468                                                         APPT_TICK(newa),
469                                                         newa->period, ordinal);
470                                 /* if enddate exist, adjust it */
471                                 if (olda->period.enddate &&
472                                     newa->period.period == olda->period.period
473                                     && newa->period.nth == olda->period.nth) {
474                                         newa->period.enddate +=
475                                             (_DtCmsBeginOfDay(APPT_TICK(newa)) -
476                                              _DtCmsBeginOfDay(APPT_TICK(olda)));
477                                 }
478                         }
479                 }
480
481                 /* ????? */
482                 /* We use the same exception list for the
483                  * new appt.  ?? is this reasonable for
484                  * all cases ??
485                  */
486                 newa->exception = _DtCm_copy_excpt4(olda->exception);
487         }
488
489         /* adjust start date */
490         _DtCms_adjust_appt_startdate(newa);
491
492         /* make copy of new appointment */
493         if ((newcopy = _DtCm_copy_one_appt4(newa)) == NULL) {
494                 stat = CSA_E_INSUFFICIENT_MEMORY;
495                 goto change_all_cleanup;
496         }
497
498         /* reuse the key */
499         newcopy->appt_id.key = olda->appt_id.key;
500
501         /* keep the original author */
502         free(newcopy->author);
503         if ((newcopy->author = strdup(olda->author)) == NULL) {
504                 stat = CSA_E_INSUFFICIENT_MEMORY;
505                 goto change_all_cleanup;
506         }
507
508         if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) {
509                 goto change_all_cleanup;
510         }
511         newa->appt_id.key = newcopy->appt_id.key;
512
513         if (oldappt)
514                 *oldappt = olda;
515         else
516                 _DtCm_free_appt4(olda);
517
518         return (stat);
519
520 change_all_cleanup:
521         if (newcopy)
522                 _DtCm_free_appt4(newcopy);
523         _DtCmsTruncateFile(cal->calendar, file_size);
524         (void)_DtCmsInsertAppt(cal, olda);
525         return (stat);
526 }
527
528 extern CSA_return_code
529 _DtCmsChangeSome(
530         _DtCmsCalendar  *cal,
531         char            *source,
532         uint            access,
533         Id_4            *p_key,
534         Appt_4          *newa,
535         Options_4       option,
536         Appt_4          **oldappt)
537 {
538         CSA_return_code stat;
539         Appt_4          *olda, *newcopy = NULL;
540         List_node       *lnode;
541         Exception_4     *newexcept;
542         int             file_size;
543         int             remain;
544
545         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
546             != CSA_SUCCESS)
547                 return (stat);
548
549         /*
550          * first, remove the old appointment from internal data structure
551          * and the callog file
552          */
553         if ((stat = _DtCmsDeleteApptInstancesAndLog(cal, source, access,
554             p_key, option, &remain, &olda)) != CSA_SUCCESS)
555                 return (stat);
556
557         /* adjust start date */
558         _DtCms_adjust_appt_startdate(newa);
559
560         /* make copy of new appointment */
561         newcopy = _DtCm_copy_one_appt4(newa);
562
563         if (option == do_forward_4) {
564                 /* if repeating info is not changed, replace
565                  * the deleted part with the new series, i.e.,
566                  * ntimes of new series _DtCmsIsEquals to the number
567                  * of deleted instances. Also get new 
568                  * exception list.      
569                  */
570                 if (olda->period.period == newa->period.period &&
571                     olda->period.nth == newa->period.nth &&
572                     olda->ntimes == newa->ntimes) { 
573
574                         newcopy->ntimes = remain;
575                         if (olda->exception != NULL) {
576                                 _DtCm_free_excpt4(newcopy->exception);
577                                 if ((stat = _DtCmsTruncateElist(olda, remain,
578                                     &newcopy->exception)) != CSA_SUCCESS) {
579                                         goto change_some_cleanup;
580                                 }
581                         }
582                 }
583         }
584
585         /* keep the original author */
586         free(newcopy->author);
587         if ((newcopy->author = strdup(olda->author)) == NULL) {
588                 stat = CSA_E_INSUFFICIENT_MEMORY;
589                 goto change_some_cleanup;
590         }
591
592         APPT_KEY(newcopy) = 0;
593
594         if ((stat = _DtCmsInsertApptAndLog(cal, newcopy)) != CSA_SUCCESS) {
595                 goto change_some_cleanup;
596         } else
597                 newa->appt_id.key = newcopy->appt_id.key;
598
599         if (oldappt)
600                 *oldappt = olda;
601         else
602                 _DtCm_free_appt4(olda);
603
604         return (stat);
605
606 change_some_cleanup:
607         if (newcopy)
608                 _DtCm_free_appt4(newcopy);
609         _DtCmsTruncateFile(cal->calendar, file_size);
610         (void)_DtCmsInsertAppt(cal, olda);
611         return (stat);
612 }
613
614 /*
615  * the passed in appt is stored in the internal data structure
616  */
617 extern CSA_return_code
618 _DtCmsInsertApptAndLog(_DtCmsCalendar *cal, Appt_4 *appt)
619 {
620         CSA_return_code stat;
621
622         if ((stat = _DtCmsInsertAppt(cal, appt)) == CSA_SUCCESS) {
623
624                 /* Add the new appointment to the log */
625                 if ((stat = v4_transact_log(cal->calendar, appt,
626                     _DtCmsLogAdd)) != CSA_SUCCESS) {
627
628                         (void)_DtCmsDeleteAppt(cal, NULL, 0, &appt->appt_id,
629                                 &appt);
630                 }
631         }
632         return (stat);
633 }
634
635 extern _DtCmsComparisonResult
636 _DtCmsCompareAppt(Id_4 *key, caddr_t data)
637 {
638         Appt_4 *appt = (Appt_4 *)data;
639
640         /* Composite key */
641         if (key->tick < appt->appt_id.tick)
642                 return (_DtCmsIsLess);
643         if (key->tick > appt->appt_id.tick)
644                 return (_DtCmsIsGreater);
645
646         /* tick's are _DtCmsIsEqual */
647         if (key->key < appt->appt_id.key)
648                 return (_DtCmsIsLess);
649         if (key->key > appt->appt_id.key)
650                 return (_DtCmsIsGreater);
651
652         return (_DtCmsIsEqual);
653 }
654
655 extern _DtCmsComparisonResult
656 _DtCmsCompareRptAppt(Id_4 *key, caddr_t data)
657 {
658         Appt_4 *appt = (Appt_4 *)data;
659
660         if (key->key < appt->appt_id.key)
661                 return (_DtCmsIsLess);
662         if (key->key > appt->appt_id.key)
663                 return (_DtCmsIsGreater);
664         return (_DtCmsIsEqual);
665 }
666
667 extern caddr_t
668 _DtCmsGetApptKey (caddr_t data)
669 {
670         return ((caddr_t) &(((Appt_4 *) data)->appt_id));
671 }
672
673 extern CSA_return_code
674 v4_transact_log(char *calendar, Appt_4 *a, _DtCmsLogOps op)
675 {
676         CSA_return_code stat;
677         char    *log=NULL;
678
679         if ((log = _DtCmsGetLogFN(calendar)) == NULL)
680                  return (CSA_E_INSUFFICIENT_MEMORY);
681         else {
682                 stat = _DtCmsAppendAppt4ByFN(log, a, op);
683                 free(log);
684         }
685         return(stat);
686 }
687
688 extern CSA_return_code
689 _DtCmsLookupRangeV4(
690         _DtCmsCalendar  *cal,
691         char            *user,
692         uint            access,
693         Range_4         *p_range,
694         boolean_t       no_end_time_range,
695         long            end1,
696         long            end2,
697         boolean_t       (*match_func)(),
698         uint            num_attrs,
699         cms_attribute   *attrs,
700         CSA_enum        *ops,
701         Appt_4          **appt_r,
702         Abb_Appt_4      **abbr_r)
703 {
704         CSA_return_code stat = CSA_SUCCESS;
705         Period_4        period;
706         caddr_t         ilp = NULL;
707         int             tmp_tick, endtick;
708         Id_4            lo_key;
709         int             n;
710         List_node       *p_lnode;
711         time_t          hi_tick, lo_tick;
712         int             tick;
713         int             ordinal;
714         int             ntimes;
715         Appt_4          *p_appt;
716         cms_entry       *entries;
717         CSA_return_code (*add_list_func)();
718
719         /* do lookup on new format calendar */
720         if (cal->fversion > 1) {
721                 if ((stat = _DtCmsLookupEntries(cal, user, access,
722                     p_range->key1, p_range->key2, no_end_time_range, end1, end2,
723                     num_attrs, attrs, ops, &entries)) != CSA_SUCCESS) {
724                         return (stat);
725                 }
726
727                 if (appt_r)
728                         stat = _DtCmsCmsentriesToAppt4ForClient(entries,
729                                 appt_r);
730                 else
731                         stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries,
732                                 abbr_r);
733
734                 _DtCm_free_cms_entries(entries);
735                 return (stat);
736         }
737
738         if (appt_r)
739                 add_list_func = _AddToLinkedAppts;
740         else
741                 add_list_func = _AddToLinkedAbbrAppts;
742
743         while (p_range != NULL)
744         {
745                 lo_key.key = MAXINT;
746                 lo_key.tick = p_range->key1;
747                 hi_tick = p_range->key2;
748
749                 if (debug)
750                 {
751                         fprintf(stderr,"Range lookup from %s",
752                                 ctime(&lo_key.tick));
753                         fprintf(stderr, " to %s\n", ctime(&hi_tick));
754                 }
755
756                 n = 0;
757
758                 /* Get a range of appointments in single appointment tree */
759                 while ((p_appt = (Appt_4 *)rb_lookup_next_larger(
760                     APPT_TREE(cal),(caddr_t)&lo_key))
761                     && (APPT_TICK(p_appt) < hi_tick))
762                 {
763                         n++;
764
765                         endtick = p_appt->appt_id.tick + p_appt->duration;
766                         if ((!no_end_time_range && (p_appt->duration == 0 ||
767                             endtick <= end1 || endtick >= end2)) ||
768                             (match_func && !match_func(p_appt, num_attrs,
769                             attrs, ops))) {
770                                 lo_key.key = APPT_KEY(p_appt);
771                                 lo_key.tick = APPT_TICK(p_appt);
772                                 continue;
773                         }
774
775                         if ((stat = (*add_list_func)(p_appt, user,
776                             access,&ilp)) != CSA_SUCCESS) {
777                                 if (ilp != NULL) {
778                                         if (appt_r)
779                                                 _DtCm_free_appt4((Appt_4 *)ilp);
780                                         else
781                                                 _DtCm_free_abbrev_appt4(
782                                                         (Abb_Appt_4 *)ilp);
783                                         ilp = NULL;
784                                 }
785                                 break;
786                         }
787
788                         lo_key.key = APPT_KEY(p_appt);
789                         lo_key.tick = APPT_TICK(p_appt);
790                 }
791
792                 /* Get a range of events from repeating appointment list */
793                 p_lnode = REPT_LIST(cal)->root;
794                 while (p_lnode != NULL)
795                 {
796                         lo_tick = p_range->key1;
797                         hi_tick = p_range->key2;
798
799                         p_appt = (Appt_4*)p_lnode->data;
800
801                         /* calculate the last tick */
802                         if (p_lnode->lasttick == 0)
803                                 p_lnode->lasttick = _DtCms_last_tick_v4(
804                                                         APPT_TICK(p_appt),
805                                                         p_appt->period,
806                                                         p_appt->ntimes);
807
808                         if (p_lnode->lasttick <= lo_tick ||
809                             APPT_TICK(p_appt) >= hi_tick ||
810                             (!no_end_time_range && p_appt->duration == 0) ||
811                             (!no_end_time_range &&
812                              (p_lnode->lasttick+p_appt->duration) <= end1) ||
813                             (!no_end_time_range &&
814                              (p_appt->appt_id.tick+p_appt->duration) >= end2) ||
815                             (match_func &&
816                              !match_func(p_appt, num_attrs, attrs, ops)) ||
817                             _GetAccessLevel(user, access, p_appt) == private_4)
818                         { 
819                                 p_lnode = hc_lookup_next (p_lnode);
820                                 continue;
821                         }
822
823                         if (!no_end_time_range &&
824                             lo_tick < (end1 - p_appt->duration))
825                                 lo_tick = end1 - p_appt->duration;
826
827                         if (!no_end_time_range &&
828                             ((end2 - p_appt->duration) < hi_tick))
829                                 hi_tick = end2 - p_appt->duration;
830
831                         ntimes = _DtCms_get_ninstance_v4(p_appt);
832                         period = p_appt->period;
833                         for (tick = _DtCms_closest_tick_v4(lo_tick,
834                                 APPT_TICK(p_appt), period, &ordinal), ordinal--;
835                              tick < hi_tick;
836                              tick = _DtCms_next_tick_v4(tick, period))
837                         {
838                                 /* Repeater but beyond the scope */
839                                 if (++ordinal > ntimes ||
840                                     tick > p_lnode->lasttick)
841                                         break;
842
843                                 if (tick <= lo_tick ||
844                                     _DtCms_marked_4_cancellation(p_appt,
845                                     ordinal))
846                                         continue;
847
848                                 n++;
849
850                                 /* Replace the parent key by the
851                                  * current tick for the repeating event
852                                  */
853                                 tmp_tick = APPT_TICK(p_appt);
854                                 APPT_TICK(p_appt) = tick; 
855
856                                 /* Add to list, restore parent key */
857                                 stat = (*add_list_func)(p_appt, user,
858                                         access, &ilp);
859
860                                 APPT_TICK(p_appt) = tmp_tick;
861
862                                 if (stat != CSA_SUCCESS) {
863                                         if (ilp != NULL) {
864                                                 if (appt_r)
865                                                     _DtCm_free_appt4(
866                                                         (Appt_4 *)ilp);
867                                                 else
868                                                     _DtCm_free_abbrev_appt4(
869                                                         (Abb_Appt_4 *)ilp);
870                                                 ilp = NULL;
871                                         }
872                                         break;
873                                 }
874                         }
875
876                         p_lnode = hc_lookup_next (p_lnode);
877                 }
878
879                 p_range = p_range->next;
880         }
881
882         if (debug)
883                 fprintf (stderr, "Found %d entries in range lookup\n", n);
884
885         if (stat == CSA_SUCCESS) {
886                 if (appt_r)
887                         *appt_r = (Appt_4 *)ilp;
888                 else
889                         *abbr_r = (Abb_Appt_4 *)ilp;
890         }
891
892         return (stat);
893 }
894
895 extern CSA_return_code
896 _DtCmsLookupKeyrangeV4(
897         _DtCmsCalendar  *cal,
898         char            *user,
899         uint            access,
900         boolean_t       no_start_time_range,
901         boolean_t       no_end_time_range,
902         time_t          start1,
903         time_t          start2,
904         time_t          end1,
905         time_t          end2,
906         long            id,
907         boolean_t       (*match_func)(),
908         uint            num_attrs,
909         cms_attribute   *attrs,
910         CSA_enum        *ops,
911         Appt_4          **appt_r,
912         Abb_Appt_4      **abbr_r)
913 {
914         CSA_return_code stat = CSA_SUCCESS;
915         Period_4        period;
916         long            tmp_tick, endtick;
917         Id_4            lo_key;
918         int             n;
919         List_node       *p_lnode;
920         int             tick;
921         int             ordinal;
922         int             ntimes;
923         Appt_4          *p_appt, *tappt;
924         Abb_Appt_4      *tabbr;
925         cms_entry       *entries;
926         CSA_return_code (*add_list_func)();
927
928         /* do lookup on new format calendar */
929         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
930                 if ((stat = _DtCmsLookupEntriesById(cal, user, access,
931                     no_start_time_range, no_end_time_range, start1, start2,
932                     end1, end2, id, num_attrs, attrs, ops, &entries))
933                     != CSA_SUCCESS) {
934                         return (stat);
935                 }
936
937                 if (appt_r) {
938                         stat = _DtCmsCmsentriesToAppt4ForClient(entries,
939                                 &tappt);
940                         *appt_r = _AddApptInOrder(*appt_r, tappt);
941                 } else {
942                         stat = _DtCmsCmsentriesToAbbrAppt4ForClient(entries,
943                                 &tabbr);
944                         *abbr_r = _AddAbbApptInOrder(*abbr_r, tabbr);
945                 }
946
947                 _DtCm_free_cms_entries(entries);
948                 return (stat);
949         }
950
951         if (appt_r)
952                 add_list_func = _AddToLinkedAppts;
953         else
954                 add_list_func = _AddToLinkedAbbrAppts;
955
956         lo_key.key = id;
957         lo_key.tick = start1;
958
959         /* Search from repeating appointments first for optimization */
960         p_lnode = hc_lookup_node (REPT_LIST(cal), (caddr_t)&lo_key);
961         if (p_lnode != NULL) {
962                 p_appt = (Appt_4*)p_lnode->data;
963                 if ((match_func && !match_func(p_appt, num_attrs, attrs, ops))
964                     || _GetAccessLevel(user, access, p_appt) == private_4)
965                         return (CSA_SUCCESS);
966
967                 /* just return the first event */
968                 if (no_start_time_range && no_end_time_range)
969                         return ((*add_list_func)(p_appt, user, access,
970                                 (appt_r ? (caddr_t)appt_r : (caddr_t)abbr_r)));
971
972                 /* Get the range of events from this appointment. */
973                 ntimes = _DtCms_get_ninstance_v4(p_appt);
974                 period = p_appt->period;
975
976                 /* calculate the last tick */
977                 if (p_lnode->lasttick == 0)
978                         p_lnode->lasttick = _DtCms_last_tick_v4(
979                                                 APPT_TICK(p_appt),
980                                                 p_appt->period, p_appt->ntimes);
981
982                 if (p_lnode->lasttick <= start1 ||
983                     p_appt->appt_id.tick >= start2 ||
984                     (!no_end_time_range && p_appt->duration == 0) ||
985                     (!no_end_time_range &&
986                      (p_lnode->lasttick+p_appt->duration) <= end1) ||
987                     (!no_end_time_range &&
988                      (p_appt->appt_id.tick+p_appt->duration) >= end2))
989                         return (SUCCESS);
990
991                 if (!no_end_time_range && (start1 < (end1 - p_appt->duration)))
992                         start1 = end1 - p_appt->duration;
993
994                 if (!no_end_time_range && ((end2 - p_appt->duration) < start2))
995                         start2 = end2 - p_appt->duration;
996
997                 for (tick = _DtCms_closest_tick_v4(start1,
998                     APPT_TICK(p_appt), period, &ordinal), ordinal--;
999                     stat == CSA_SUCCESS && tick < start2;
1000                     tick = _DtCms_next_tick_v4(tick, period))
1001                 {
1002                         /* Repeater but beyond the scope */
1003                         if (++ordinal > ntimes || tick > p_lnode->lasttick)
1004                                 break;
1005
1006                         if (tick <= start1)
1007                                 continue;
1008
1009                         /* If not cancelled, add to linked list */
1010                         if (!_DtCms_marked_4_cancellation (p_appt, ordinal))
1011                         {
1012                                 n++;
1013
1014                                 /* Replace the parent key by
1015                                  * the current tick for the
1016                                  * repeating event
1017                                  */
1018                                 tmp_tick = APPT_TICK(p_appt);
1019                                 APPT_TICK(p_appt) = tick; 
1020
1021                                 /* Add to list, restore parent key */
1022                                 stat = (*add_list_func)(p_appt, user, access,
1023                                         (appt_r ? (caddr_t)appt_r :
1024                                         (caddr_t)abbr_r));
1025
1026                                 APPT_TICK(p_appt) = tmp_tick;
1027
1028                         }
1029                 }
1030                 return (stat);
1031         } else {
1032                 /* Check if it is in single appointment tree */
1033                 while ((p_appt = (Appt_4 *)rb_lookup_next_larger(
1034                     APPT_TREE(cal), (caddr_t) &lo_key)) &&
1035                     (APPT_TICK(p_appt) < start2))
1036                 {
1037                         if (p_appt->appt_id.key != lo_key.key) {
1038                                 lo_key.tick = APPT_TICK(p_appt);
1039                                 lo_key.key = APPT_KEY(p_appt);
1040                         } else {
1041                                 endtick = p_appt->appt_id.tick+p_appt->duration;
1042
1043                                 if ((!no_end_time_range &&
1044                                     (p_appt->duration == 0 || endtick <= end1 ||
1045                                     endtick >= end2)) ||
1046                                     (match_func && !match_func(p_appt,
1047                                     num_attrs, attrs, ops))) {
1048
1049                                         return (CSA_SUCCESS);
1050                                 } else {
1051                                         return ((*add_list_func)(p_appt, user,
1052                                                 access, (appt_r ?
1053                                                 (caddr_t)appt_r :
1054                                                 (caddr_t)abbr_r)));
1055                                 }
1056                         }
1057                 }
1058                 return (CSA_SUCCESS);
1059         }
1060 }
1061
1062 extern CSA_return_code
1063 _AddToLinkedAppts(
1064         Appt_4  *p_appt,
1065         char    *user,
1066         uint    access,
1067         caddr_t *ilp)
1068 {
1069         Appt_4  *p_prev;
1070         Appt_4  *copy;
1071
1072         switch (_GetAccessLevel(user, access, p_appt)) {
1073         case public_4:
1074                 copy = _DtCm_copy_one_appt4(p_appt);
1075                 break;
1076         case semiprivate_4:
1077                 copy = _DtCm_copy_semiprivate_appt4(p_appt);
1078                 break;
1079         default:
1080                 return (CSA_SUCCESS);
1081         }
1082
1083         if (copy == NULL)
1084                 return(CSA_E_INSUFFICIENT_MEMORY);
1085         else {
1086                 *ilp = (caddr_t)_AddApptInOrder((Appt_4 *)*ilp, copy);
1087                 return (CSA_SUCCESS);
1088         }
1089 }
1090
1091 extern CSA_return_code
1092 _AddToLinkedAbbrAppts(
1093         Appt_4  *p_appt,
1094         char    *user,
1095         uint    access,
1096         caddr_t *ilp)
1097 {
1098         Abb_Appt_4 *copy;
1099
1100         switch (_GetAccessLevel(user, access, p_appt)) {
1101         case public_4:
1102                 copy = _DtCm_appt_to_abbrev4(p_appt);
1103                 break;
1104         case semiprivate_4:
1105                 copy = _DtCm_appt_to_semiprivate_abbrev4(p_appt);
1106                 break;
1107         default:
1108                 return (CSA_SUCCESS);
1109         }
1110
1111         if (copy == NULL)
1112                 return(CSA_E_INSUFFICIENT_MEMORY);
1113         else {
1114                 *ilp = (caddr_t)_AddAbbApptInOrder((Abb_Appt_4 *)*ilp, copy);
1115                 return (CSA_SUCCESS);
1116         }
1117 }
1118
1119 extern Privacy_Level_4
1120 _GetAccessLevel(char *user, uint access, Appt_4 *p_appt)
1121 {
1122         if (access & CSA_OWNER_RIGHTS ||
1123             ((access & access_read_4) && p_appt->privacy == public_4) ||
1124             _DtCmIsSameUser(user, p_appt->author)) {
1125
1126                 return (public_4);
1127
1128         } else if (p_appt->privacy == private_4) {
1129
1130                 return (private_4);
1131
1132         } else
1133                 return (semiprivate_4);
1134
1135 }
1136
1137 extern Appt_4 *
1138 _AddApptInOrder(Appt_4 *head, Appt_4 *aptr)
1139 {
1140         Appt_4  *p_appt, *p_prev, *p_next;
1141
1142         while (aptr) {
1143                 p_next = aptr->next;
1144                 aptr->next = NULL;
1145
1146                 /* Add the item to the linked list in ascending order */
1147                 p_prev = NULL;
1148                 p_appt = head;
1149                 while (p_appt != NULL)
1150                 {
1151                         if (APPT_TICK(aptr) <= APPT_TICK(p_appt))
1152                                 break;
1153                         p_prev = p_appt;
1154                         p_appt = p_appt->next;
1155                 }
1156
1157                 if (p_prev == NULL)
1158                 {
1159                         aptr->next = p_appt;
1160                         head = aptr;
1161                 } else {
1162                         aptr->next = p_prev->next;
1163                         p_prev->next = aptr;
1164                 }
1165
1166                 aptr = p_next;
1167         }
1168
1169         return (head);
1170 }
1171
1172 extern Abb_Appt_4 *
1173 _AddAbbApptInOrder(Abb_Appt_4 *head, Abb_Appt_4 *aptr)
1174 {
1175         Abb_Appt_4      *p_appt, *p_prev, *p_next;
1176
1177         while (aptr) {
1178                 p_next = aptr->next;
1179                 aptr->next = NULL;
1180
1181                 /* Add the item to the linked list in ascending order */
1182                 p_prev = NULL;
1183                 p_appt = head;
1184                 while (p_appt != NULL)
1185                 {
1186                         if (APPT_TICK(aptr) <= APPT_TICK(p_appt))
1187                                 break;
1188                         p_prev = p_appt;
1189                         p_appt = p_appt->next;
1190                 }
1191
1192                 if (p_prev == NULL)
1193                 {
1194                         aptr->next = p_appt;
1195                         head = aptr;
1196                 } else {
1197                         aptr->next = p_prev->next;
1198                         p_prev->next = aptr;
1199                 }
1200
1201                 aptr = p_next;
1202         }
1203
1204         return (head);
1205 }
1206
1207 extern CSA_return_code
1208 _DtCmsSetV4AccessListAndLog(_DtCmsCalendar *cal, Access_Entry_4 *alist)
1209 {
1210         CSA_return_code stat;
1211         char    *name, *log;
1212         int     file_size;
1213
1214         /* update access list for old format calendar */
1215         if ((name = _DtCmsTarget2Name(cal->calendar)) == NULL)
1216                 return (CSA_E_INSUFFICIENT_MEMORY);
1217
1218         if ((log = _DtCmsGetLogFN(name)) == NULL) {
1219                 free(name);
1220                 return (CSA_E_INSUFFICIENT_MEMORY);
1221         }
1222
1223         /*
1224          * save file size in case the first log transaction
1225          * succeeds but the second log transaction fails.
1226          */
1227         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
1228             != CSA_SUCCESS) {
1229                 free (name);
1230                 free (log);
1231                 return (stat);
1232         }
1233
1234         /* Set to the data structure */
1235         if ((stat = _DtCmsSetV4AccessListInCal(cal, alist)) == CSA_SUCCESS) {
1236
1237                 if (((stat = _DtCmsAppendAccessByFN(log, access_read_4,
1238                     GET_R_ACCESS(cal))) != CSA_SUCCESS) ||
1239                     ((stat = _DtCmsAppendAccessByFN(log, access_write_4,
1240                     GET_W_ACCESS(cal))) != CSA_SUCCESS) ||
1241                     ((stat = _DtCmsAppendAccessByFN(log, access_delete_4,
1242                     GET_D_ACCESS(cal))) != CSA_SUCCESS) ||
1243                     ((stat = _DtCmsAppendAccessByFN(log, access_exec_4,
1244                     GET_X_ACCESS(cal))) != CSA_SUCCESS)) {
1245
1246                         /* rollback log file */
1247                         _DtCmsTruncateFile(cal->calendar, file_size);
1248                 } else
1249                         cal->modified = B_TRUE;
1250         }
1251
1252         free (name);
1253         free(log);
1254         return (stat);
1255 }
1256
1257 extern CSA_return_code
1258 _DtCmsGetV4Reminders(
1259         _DtCmsCalendar  *cal,
1260         long            tick,
1261         Reminder_4      **rem_r,
1262         _DtCmsEntryId   **ids_r)
1263 {
1264         Rm_que          *p_node, *p_prev, *p_next, *p_new;
1265         Reminder_4      *rptr, *v4rem = NULL;
1266         _DtCmsEntryId   *idptr, *ids = NULL;
1267
1268         if (rem_r == NULL) return (CSA_E_INVALID_PARAMETER);
1269
1270         /* do lookup in old format calendar */
1271         p_prev = NULL;
1272         p_node = cal->rm_queue;
1273         while (p_node != NULL)
1274         {
1275                 /* It is still a future reminder. */
1276                 if (tick < p_node->remind_at)
1277                         break;
1278
1279                 /* The reminder expired.  It either needs to be recalculated
1280                  * (repeating appointment) or dropped (non-repeating appt.)
1281                  */
1282                 p_next = _DtCmsRemoveReminderV4(&cal->rm_queue, p_prev, p_node);
1283
1284                 if (is_repeater(p_node->appt)) {
1285                         /* Calculate next reminder for repeating appointment */
1286                         p_new = build_reminder(tick+1, p_node->appt,
1287                                         p_node->attr, p_node->remind_at,
1288                                         p_node->remind_ord);
1289                         if (p_new != NULL)
1290                         {
1291                                 _DtCmsAddReminderV4(&cal->rm_queue, p_new);
1292                                 if (p_new->next == p_next)
1293                                         p_prev = p_new;
1294                         }
1295                 }
1296
1297                 free (p_node);
1298                 p_node = p_next;
1299         }
1300
1301         /* Pick the first one from the active reminder queue because it is
1302          * always >= the given key.
1303          */
1304         p_node = cal->rm_queue;
1305         if (p_node != NULL)
1306         {
1307                 tick = p_node->remind_at;
1308                 do
1309                 {
1310                         rptr = _DtCmsGetReminderInfoV4(p_node);
1311                         rptr->next = v4rem;
1312                         v4rem = rptr;
1313                         if (ids_r) {
1314                                 idptr = (_DtCmsEntryId *)calloc(1,
1315                                         sizeof(_DtCmsEntryId));
1316                                 idptr->id = p_node->appt->appt_id.key;
1317                                 idptr->next = ids;
1318                                 ids = idptr;
1319                         }
1320                         p_node = p_node->next;
1321                 } while ((p_node != NULL) && (p_node->remind_at == tick));
1322         }
1323
1324         *rem_r = v4rem;
1325         if (ids_r) *ids_r = ids;
1326         return (CSA_SUCCESS);
1327 }
1328
1329 extern void
1330 _DtCmsFreeEntryIds(_DtCmsEntryId *ids)
1331 {
1332         _DtCmsEntryId *ptr;
1333
1334         while (ids != NULL) {
1335                 ptr = ids->next;
1336                 free(ids);
1337                 ids = ptr;
1338         }
1339 }
1340
1341 extern CSA_return_code
1342 _DtCmsTruncateElist(Appt_4 *parent_p, int remain, Except_4 **excpt)
1343 {
1344         int except_no, ntimes_diff;
1345         Except_4 *e, *p, *last_e = NULL, *head = NULL;
1346
1347         ntimes_diff = _DtCms_get_ninstance_v4(parent_p) - remain;
1348         p = parent_p->exception;
1349         while(p != NULL) {
1350                 if ((except_no = (p->ordinal - ntimes_diff)) > 0 && 
1351                         except_no <= remain) {
1352                         if ((e = (Except_4*)calloc(1, sizeof(Except_4)))
1353                             == NULL) {
1354                                 _DtCm_free_excpt4(head);
1355                                 return (CSA_E_INSUFFICIENT_MEMORY);
1356                         }
1357                         e->ordinal = except_no;
1358                         e->next = NULL;
1359                         if (last_e != NULL) 
1360                                 last_e->next = e;
1361                         else
1362                                 head = e;
1363                         last_e = e;
1364                 }
1365                 p = p->next;
1366         }
1367
1368         *excpt = head;
1369         return (CSA_SUCCESS);
1370 }
1371
1372 extern CSA_return_code 
1373 _DtCmsSetV4AccessListInCal(_DtCmsCalendar *cal, Access_Entry_4 *e)
1374 {
1375         Access_Entry_4  *q;
1376
1377         /* Wipe out the old access lists. */
1378         _DtCm_free_access_list4(GET_R_ACCESS (cal));
1379         _DtCm_free_access_list4(GET_W_ACCESS (cal));
1380         _DtCm_free_access_list4(GET_D_ACCESS (cal));
1381         _DtCm_free_access_list4(GET_X_ACCESS (cal));
1382         SET_R_ACCESS(cal, NULL);
1383         SET_W_ACCESS(cal, NULL);
1384         SET_D_ACCESS(cal, NULL);
1385         SET_X_ACCESS(cal, NULL);
1386         _DtCm_free_access_list4(cal->alist);
1387
1388         /* Split the access list to 3 differnt operation lists */
1389         while (e != NULL)
1390         {
1391                 if (e->access_type & access_read_4) {
1392                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1393                         q->next = GET_R_ACCESS(cal);
1394                         SET_R_ACCESS(cal, q);
1395                 }
1396                 if (e->access_type & access_write_4) {
1397                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1398                         q->next = GET_W_ACCESS(cal);
1399                         SET_W_ACCESS(cal, q);
1400                 }
1401                 if (e->access_type & access_delete_4) {
1402                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1403                         q->next = GET_D_ACCESS(cal);
1404                         SET_D_ACCESS(cal, q);
1405                 }
1406                 if (e->access_type & access_exec_4) {
1407                         q = _DtCm_make_access_entry4(e->who, e->access_type);
1408                         q->next = GET_X_ACCESS(cal);
1409                         SET_X_ACCESS(cal, q);
1410                 }
1411                 e = e->next;
1412         }
1413
1414         cal->alist = _DtCmsCalendarAccessList(cal);
1415
1416         return (CSA_SUCCESS);
1417 }
1418
1419 /******************************************************************************
1420  * static functions used within the file
1421  ******************************************************************************/
1422
1423 static Exception_4
1424 append_exception_list(Appt_4 *p_appt, int ordinal)
1425 {
1426         Exception_4 p_excpt;
1427         Exception_4 p_prev;
1428         Exception_4 p_ex;
1429
1430         if ((p_excpt = (Exception_4)calloc(1, sizeof(*p_excpt))) == NULL)
1431                 return (NULL);
1432         p_excpt->ordinal = ordinal;
1433         p_prev = NULL;
1434         p_ex = p_appt->exception;
1435         while (p_ex != NULL)
1436         {
1437                 /* Exception list is in descending order for faster access */
1438                 if (ordinal > p_ex->ordinal)
1439                         break;
1440                 p_prev = p_ex;
1441                 p_ex = p_ex->next;
1442         }
1443         if (p_prev == NULL)
1444         {
1445                 p_excpt->next = p_appt->exception;
1446                 p_appt->exception = p_excpt;
1447         }
1448         else
1449         {
1450                 p_excpt->next = p_prev->next;
1451                 p_prev->next = p_excpt;
1452         }
1453
1454         return (p_excpt);
1455 }
1456
1457 /*
1458  * remove exceptions that are larger than ordinal
1459  */
1460 static void
1461 trunc_exception_list(Appt_4 *p_appt, int ordinal)
1462 {
1463         Exception_4 p_next;
1464         Exception_4 p_ex;
1465
1466         p_ex = p_appt->exception;
1467
1468         /* Exception list is in descending order for faster access */
1469         while ((p_ex != NULL) && (p_ex->ordinal > ordinal))
1470         {
1471                 p_next = p_ex->next;
1472                 free(p_ex);
1473                 p_ex = p_next;
1474         }
1475         p_appt->exception = p_ex;
1476 }
1477
1478 static int
1479 num_exception(Appt_4 *p_appt)
1480 {
1481         int ntimes;
1482         Except_4 *p;
1483
1484         ntimes = 0;
1485         p = p_appt->exception;
1486         while (p != NULL)
1487         {
1488                 ntimes++;
1489                 p = p->next;
1490         }
1491         return (ntimes);
1492 }
1493