dtcm: Resolve CID 87713
[oweals/cde.git] / cde / programs / dtcm / server / reminder.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: reminder.c /main/4 1995/11/09 12:48:20 rswiston $ */
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 <string.h>
35 #include <time.h>
36 #include "cmscalendar.h"
37 #include "reminder.h"           
38 #include "appt4.h"
39 #include "repeat.h"
40 #include "v4ops.h"
41 #include "v5ops.h"
42 #include "rerule.h"
43 #include "reutil.h"
44 #include "iso8601.h"
45
46 /******************************************************************************
47  * forward declaration of static functions used within the file
48  ******************************************************************************/
49
50 static _DtCmsRemInfo *_BuildReminder4Entry(
51                         cms_entry       *entry,
52                         int             aindex,
53                         List_node       *lnode,
54                         time_t          cutoff,
55                         _DtCmsRemInfo   **active);
56
57 static void _InsertReminder(
58                         _DtCmsRemInfo   **head,
59                         _DtCmsRemInfo   *rem);
60
61 static CSA_return_code _GetNextReminders(
62                         _DtCmsRemQueue          *remq,
63                         time_t                  tick,
64                         cms_reminder_ref        **rems);
65
66 static CSA_return_code _GetNextRemindersFromQ(
67                         _DtCmsRemQueue          *remq,
68                         int                     qindex,
69                         time_t                  tick,
70                         cms_reminder_ref        **rems);
71
72 static cms_reminder_ref *_GetReminderRefFromInfo(
73                         _DtCmsRemInfo   *rem,
74                         time_t          starttime,
75                         time_t          runtime);
76
77 static void _RemoveReminderFromQ(
78                         _DtCmsRemQueue  *remq,
79                         int             qindex,
80                         cms_entry       *entry,
81                         List_node       *lnode,
82                         time_t          starttime,
83                         boolean_t       delfwd);
84
85 static _DtCmsRemInfo *_RemoveReminderFromList(
86                         _DtCmsRemInfo   **remq,
87                         cms_entry       *entry,
88                         time_t          starttime,
89                         boolean_t       delfwd);
90
91 static long _GetNextActiveTick(
92                         cms_entry       *entry,
93                         time_t          target,
94                         time_t          lasttick,
95                         RepeatEvent     *re);
96
97 static void _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex);
98
99 static CSA_return_code _GetNextRemindersFromList(
100                         _DtCmsRemInfo           *rlist,
101                         time_t                  giventime,
102                         cms_reminder_ref        **rf_r);
103
104 static void _DtCmsAddReminder4EntryToQ(
105                         _DtCmsRemQueue  *remq,
106                         cms_entry       *entry,
107                         int             aindex,
108                         List_node       *lnode);
109
110 /*****************************************************************************
111  * extern functions used in the library
112  *****************************************************************************/
113
114 extern void
115 _DtCmsAddReminderV4(Rm_que **qhead, Rm_que *p_reminder)
116 {
117         Rm_que  *p_prev;
118         Rm_que  *p_node;
119
120         if (p_reminder == NULL)
121                 return;
122
123         p_prev = NULL;
124         p_node = *qhead;
125         while (p_node != NULL)
126         {
127                 if (p_reminder->remind_at < p_node->remind_at)
128                         break;
129                 p_prev = p_node;
130                 p_node = p_node->next;
131         }
132
133         if (p_prev == NULL) {
134                 p_reminder->next = *qhead;
135                 *qhead = p_reminder;
136         } else {
137                 p_reminder->next = p_prev->next;
138                 p_prev->next = p_reminder;
139         }
140 }
141
142 extern Rm_que *
143 _DtCmsRemoveReminderV4(Rm_que **qhead, Rm_que *p_prev, Rm_que *p_curr)
144 {
145         if (p_prev == NULL)
146                 *qhead = p_curr->next;
147         else
148                 p_prev->next = p_curr->next;
149         return (p_curr->next);
150 }
151
152 extern Rm_que *
153 build_reminder(
154         time_t  current_time,
155         Appt_4  *p_appt,
156         Attr_4  p_attr,
157         time_t  start_tick,
158         u_int   start_ord)
159 {
160         int     ntimes;
161         Period_4 period;
162         Rm_que  *p_reminder = NULL;
163
164         /* Ignore the expired or unqualified reminder. */
165         p_reminder = NULL;
166         if (is_appointment(p_appt)) {
167                 /* The event is not expired yet, build the reminder */
168                 if (start_tick >= current_time)
169                 {
170                         if ((p_reminder = (Rm_que *)calloc(1, sizeof(Rm_que)))
171                            == NULL)
172                                 return (NULL);
173
174                         p_reminder->remind_ord = 0;
175                 }
176         } else {
177                 period = p_appt->period;
178                 ntimes = _DtCms_get_ninstance_v4(p_appt);
179                 while (start_ord <= ntimes) {
180                         /* Event is not expired */
181                         if (start_tick >= current_time) {
182                                 /* Event is not cancelled */
183                                 if (!_DtCms_marked_4_cancellation (p_appt, start_ord))
184                                 {
185                                         if ((p_reminder = (Rm_que *)calloc(1,
186                                             sizeof(Rm_que))) == NULL)
187                                                 return (NULL);
188
189                                         p_reminder->remind_ord = start_ord;
190                                         break;
191                                 }
192                         }
193                         /* Event is expired, advance to next event */
194                         start_tick = _DtCms_next_tick_v4 (start_tick, period);
195                         start_ord++;
196                 }
197         }
198
199         if (p_reminder != NULL) {
200                 p_reminder->remind_at = start_tick;
201                 p_reminder->appt = p_appt;
202                 p_reminder->attr = p_attr;
203         }
204
205         return (p_reminder);
206 }
207
208 extern Reminder_4 *
209 _DtCmsGetReminderInfoV4(Rm_que  *original)
210 {
211         Reminder_4 *copy;
212
213         if (original == NULL)
214                 return (NULL);
215         
216         if ((copy = calloc(1, sizeof(Reminder_4))) != NULL) {
217                 copy->tick = original->remind_at;
218                 copy->next = NULL;
219
220                 if ((copy->attr.attr = strdup(original->attr->attr)) == NULL) {
221                         free(copy);
222                         return (NULL);
223                 }
224
225                 if ((copy->attr.value = strdup(original->attr->value)) == NULL)
226                 {
227                         _DtCm_free_reminder4(copy);
228                         return (NULL);
229                 }
230
231                 if ((copy->attr.clientdata = strdup(original->attr->clientdata))
232                     == NULL) {
233                         _DtCm_free_reminder4(copy);
234                         return (NULL);
235                 }
236
237                 copy->attr.next = NULL;
238
239                 copy->appt_id.tick = copy->tick + atol (copy->attr.value);
240                 copy->appt_id.key = original->appt->appt_id.key;
241         }
242
243         return (copy);
244 }
245
246 extern void
247 _DtCmsPrintReminderListV4(Rm_que *qhead)
248 {
249         Rm_que  *p_node = qhead;
250         char    *temp=NULL;
251
252         if (qhead == NULL)
253                 return;
254
255         fprintf (stderr, "--- Active Reminder Queue ---\n");
256         while (p_node != NULL) {
257
258                 if (temp = strchr(p_node->appt->what, '\n'))
259                         *temp = '\0';
260
261                 fprintf(stderr, "%s (%d) %s: %s\n", ctime(&p_node->remind_at),
262                         p_node->remind_ord, p_node->attr->attr,
263                         p_node->appt->what);
264
265                 if (temp)
266                         *temp = '\n';
267
268                 p_node = p_node->next;
269         }
270 }
271
272 /*
273  * Obsolete all reminders (iff ord == 0) whose parent appointment matches a
274  * given appointment.  If ord != 0, then obsolete all active reminders whose
275  * serving ordinal matches ord in additional to the matching of its parent
276  * appointment.  The reminder of the next available instance will be put on the
277  * reminder queue.
278  */
279 extern void
280 _DtCmsObsoleteReminderV4(
281         Rm_que **qhead, 
282         Appt_4 *p_appt, 
283         int ord, 
284         boolean_t delforward)
285 {
286         Rm_que *p_prev;
287         Rm_que *p_next;
288         Rm_que *p_node;
289         Rm_que *p_hdr = NULL;
290
291         p_prev = NULL;
292         p_node = *qhead;
293         while (p_node != NULL) {
294
295                 if ((p_node->appt != p_appt) ||
296                     ((ord != 0) && (ord != p_node->remind_ord)) ||
297                     ((ord != 0) && delforward && p_node->remind_ord < ord)) {
298
299                         p_next = p_node->next;
300
301                 } else {
302                         /* Found the obsolete reminder. */
303                         p_next = _DtCmsRemoveReminderV4 (qhead,p_prev,p_node);
304
305                         if (ord == 0)
306                                 free (p_node);
307                         else {
308                                 /* Chain the obsolete reminders together to
309                                  * re-calculate the new reminders.
310                                  */
311                                 p_node->next = p_hdr;
312                                 p_hdr = p_node;
313                         }
314
315                         p_node = p_prev;
316                 }
317
318                 p_prev = p_node;
319                 p_node = p_next;
320         }
321
322         /* Build the reminders of the next instance from obsoleted reminders.
323          * Note, we can't put this code in the above 'while'-loop because it
324          * may confuse the loop.
325          */
326         while (p_hdr != NULL) {
327
328                 p_next = p_hdr->next;
329                 p_node = build_reminder(p_hdr->remind_at+1,
330                                         p_hdr->appt, p_hdr->attr,
331                                         p_hdr->remind_at, ord);
332                 _DtCmsAddReminderV4 (qhead, p_node);
333                 free (p_hdr);
334                 p_hdr = p_next;
335         }
336 }
337
338 #define _DtCms_NUM_REMINDERS    4
339
340 extern void
341 _DtCmsAddReminders4Entry(
342         _DtCmsRemQueue  **remq,
343         cms_entry       *entry,
344         List_node       *lnode)
345 {
346         _DtCmsRemQueue  *queue;
347         int             i;
348
349         if (*remq == NULL) {
350                 if ((queue = (_DtCmsRemQueue *)calloc(1,
351                     sizeof(_DtCmsRemQueue))) == NULL)
352                         return;
353
354                 /* initialize queues for the cde defined reminders */
355                 if ((queue->aindex = (int *)malloc(sizeof(int) *
356                     _DtCms_NUM_REMINDERS)) == NULL) {
357                         free(queue);
358                         return;
359                 }
360
361                 if ((queue->names = (char **)malloc(sizeof(char *) *
362                     _DtCms_NUM_REMINDERS)) == NULL) {
363                         free(queue->aindex);
364                         free(queue);
365                         return;
366                 }
367
368                 if ((queue->active = (_DtCmsRemInfo **)calloc(1, 
369                     sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
370                         free(queue->names);
371                         free(queue->aindex);
372                         free(queue);
373                         return;
374                 }
375
376                 if ((queue->oldhead = (_DtCmsRemInfo **)calloc(1, 
377                     sizeof(_DtCmsRemInfo) * _DtCms_NUM_REMINDERS)) == NULL) {
378                         free(queue->active);
379                         free(queue->names);
380                         free(queue->aindex);
381                         free(queue);
382                         return;
383                 }
384
385                 queue->num_queues = _DtCms_NUM_REMINDERS;
386                 queue->aindex[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER_I;
387                 queue->names[0] = CSA_ENTRY_ATTR_AUDIO_REMINDER;
388                 queue->aindex[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER_I;
389                 queue->names[1] = CSA_ENTRY_ATTR_FLASHING_REMINDER;
390                 queue->aindex[2] = CSA_ENTRY_ATTR_MAIL_REMINDER_I;
391                 queue->names[2] = CSA_ENTRY_ATTR_MAIL_REMINDER;
392                 queue->aindex[3] = CSA_ENTRY_ATTR_POPUP_REMINDER_I;
393                 queue->names[3] = CSA_ENTRY_ATTR_POPUP_REMINDER;
394
395                 /* set cutoff to be half an hour earlier than now
396                  * to compensate time difference between machines
397                  */
398                 queue->cutoff = time(0) - 60 * 30;
399
400                 *remq = queue;
401         }
402
403         /* Add the qualified reminder attrs to the reminder queue */
404         for (i = 1; i <= entry->num_attrs; i++) {
405
406                 if (entry->attrs[i].value == NULL ||
407                     entry->attrs[i].value->type != CSA_VALUE_REMINDER) 
408                         continue;
409
410                 _DtCmsAddReminder4EntryToQ(*remq, entry, i, lnode);
411         }
412 }
413
414 extern void
415 _DtCmsObsoleteReminder4Entry(
416         _DtCmsRemQueue  *remq,
417         cms_entry       *entry,
418         List_node       *lnode,
419         time_t          starttime,
420         boolean_t       delfwd)
421 {
422         int     i;
423
424         for (i = 0; i < remq->num_queues; i++) {
425                 if (entry->attrs[remq->aindex[i]].value)
426                         _RemoveReminderFromQ(remq, i, entry, lnode,
427                                 starttime, delfwd);
428         }
429 }
430
431 extern CSA_return_code
432 _DtCmsLookupReminder(
433         _DtCmsRemQueue          *remq,
434         time_t                  tick,
435         uint                    num_names,
436         cms_attr_name           *names,
437         cms_reminder_ref        **rems)
438 {
439         int             i, j;
440         CSA_return_code stat;
441
442         *rems = NULL;
443
444         if (remq == NULL)
445                 return (CSA_SUCCESS);
446
447         if (num_names == 0)
448                 return (_GetNextReminders(remq, tick, rems));
449
450         for (i = 0; i < num_names; i++) {
451                 for (j = 0; j < remq->num_queues; j++) {
452                         if (strcmp(names[i].name, remq->names[j]) == 0) {
453                                 names[i].num = remq->aindex[j];
454
455                                 if ((stat = _GetNextRemindersFromQ(remq, i,
456                                     tick, rems)) != CSA_SUCCESS) {
457                                         if (*rems)
458                                                 _DtCmsFreeReminderRef(*rems);
459                                         return (stat);
460                                 }
461                         }
462                 }
463         }
464
465         return (CSA_SUCCESS);
466 }
467
468 extern void
469 _DtCmsFreeReminderRef(cms_reminder_ref *rems)
470 {
471         cms_reminder_ref        *next;
472
473         while (rems != NULL) {
474                 next = rems->next;
475
476                 if (rems->reminder_name)
477                         free(rems->reminder_name);
478                 if (rems->entryid)
479                         free(rems->entryid);
480
481                 free(rems);
482                 rems = next;
483         }
484 }
485
486 extern void
487 _DtCmsUpdateReminders(_DtCmsRemQueue *remq)
488 {
489         int     i;
490
491         remq->cutoff = time(0) - 60*30;
492
493         for (i = 0; i < remq->num_queues; i++)
494                 _UpdateReminderQ(remq, i);
495 }
496
497 /*****************************************************************************
498  * static functions used within the file
499  *****************************************************************************/
500
501 static _DtCmsRemInfo *
502 _BuildReminder4Entry(
503         cms_entry       *entry,
504         int             aindex,
505         List_node       *lnode,
506         time_t          cutoff,
507         _DtCmsRemInfo   **active)
508 {
509         _DtCmsRemInfo   *rptr, *rptr2;
510         time_t          lead;
511         time_t          tick;
512         RepeatEventState *restate;
513
514         if (active) *active = NULL;
515
516         if ((rptr = (_DtCmsRemInfo *)calloc(1, sizeof(_DtCmsRemInfo))) == NULL)
517                 return (NULL);
518
519         rptr->lnode = lnode;
520         rptr->isentry = B_TRUE;
521         rptr->data.e = entry;
522         rptr->rem.i = entry->attrs[aindex].name.num;
523
524         _csa_iso8601_to_duration(entry->attrs[aindex].value->item.\
525                         reminder_value->lead_time, &lead);
526
527         if (lnode == NULL) {
528                 rptr->starttime = entry->key.time;
529                 rptr->runtime = entry->key.time - lead;
530         } else {
531                 if (lnode->lasttick == 0) {
532                         lnode->lasttick = LastTick(entry->key.time, lnode->re);
533                         lnode->duration = _DtCmsGetDuration(entry);
534                 }
535                 rptr->lasttick = lnode->lasttick;
536
537                 /* calculate first tick */
538                 tick = _GetNextActiveTick(entry, entry->key.time,
539                         lnode->lasttick, lnode->re);
540
541                 rptr->starttime = tick;
542                 rptr->runtime = tick - lead;
543
544                 /* need to calculate the active tick if
545                  * 1. runtime of first tick is before cutoff time,
546                  * 2. runtime of last tick is after cutoff time, and
547                  * 3. there's reminders for instances after the cutoff time
548                  */
549                 if (active && rptr->runtime < cutoff &&
550                     (lnode->lasttick - lead >= cutoff) &&
551                     (tick = _GetNextActiveTick(entry, cutoff + lead,
552                     lnode->lasttick, lnode->re)) > 0)
553                 {
554                         if ((rptr2 = (_DtCmsRemInfo *)calloc(1,
555                             sizeof(_DtCmsRemInfo))) == NULL) {
556                                 free(rptr);
557                                 return (NULL);
558                         }
559
560                         rptr2->lnode = lnode;
561                         rptr2->isentry = B_TRUE;
562                         rptr2->data.e = entry;
563                         rptr2->rem.i = entry->attrs[aindex].name.num;
564                         rptr2->lasttick = lnode->lasttick;
565                         rptr2->starttime = tick;
566                         rptr2->runtime = tick - lead; 
567                         *active = rptr2;
568                 }
569         }
570
571         return (rptr);
572 }
573
574 static void
575 _InsertReminder(
576         _DtCmsRemInfo   **head,
577         _DtCmsRemInfo   *rem)
578 {
579         _DtCmsRemInfo   *rptr, *prev;
580
581         for (rptr = *head, prev = NULL; rptr != NULL;
582             prev = rptr, rptr = rptr->next) {
583                 if (rem->runtime < rptr->runtime)
584                         break;
585         }
586
587         if (rptr == NULL) {
588                 if (*head == NULL) {
589                         *head = rem;
590                 } else
591                         prev->next = rem;
592         } else {
593                 rem->next = rptr;
594
595                 if (prev == NULL)
596                         *head = rem;
597                 else
598                         prev->next = rem;
599         }
600 }
601
602 static CSA_return_code
603 _GetNextReminders(_DtCmsRemQueue *remq, time_t tick, cms_reminder_ref **rems)
604 {
605         CSA_return_code         stat;
606         cms_reminder_ref        *rptr, *head, *tail;
607         int                     i;
608
609         for (i = 0, head = NULL; i < remq->num_queues; i++) {
610                 rptr = NULL;
611
612                 if ((stat = _GetNextRemindersFromQ(remq, i, tick, &rptr))
613                     != CSA_SUCCESS) {
614                         if (head)
615                                 _DtCmsFreeReminderRef(head);
616                         return (stat);
617                 }
618
619                 if (rptr == NULL)
620                         continue;
621
622                 if (head == NULL)
623                         head = tail = rptr;
624                 else if (rptr->runtime == head->runtime) {
625                         /* combine the list */
626                         for (; tail->next != NULL; tail = tail->next);
627
628                         tail->next = rptr;
629                         tail = rptr;
630                 } else if (rptr->runtime < head->runtime) {
631                         _DtCmsFreeReminderRef(head);
632                         head = tail = rptr;
633                 } else
634                         _DtCmsFreeReminderRef(rptr);
635         }
636
637         if (head) {
638                 *rems = head;
639         }
640
641         return (CSA_SUCCESS);
642 }
643
644 static CSA_return_code
645 _GetNextRemindersFromQ(
646         _DtCmsRemQueue          *remq,
647         int                     qindex,
648         time_t                  tick,
649         cms_reminder_ref        **rems)
650 {
651         CSA_return_code         stat;
652         cms_reminder_ref        *rem = *rems;
653
654         if (tick >= remq->cutoff)
655                 return (_GetNextRemindersFromList(remq->active[qindex], tick,
656                         rems));
657         else {
658                 if ((stat = _GetNextRemindersFromList(remq->oldhead[qindex],
659                     tick, rems)) == CSA_SUCCESS && rem == *rems)
660                         return (_GetNextRemindersFromList(remq->active[qindex],
661                                 tick, rems));
662                 else
663                         return (stat);
664         }
665 }
666
667 static cms_reminder_ref *
668 _GetReminderRefFromInfo(_DtCmsRemInfo *rem, time_t starttime, time_t runtime)
669 {
670         cms_reminder_ref        *rptr;
671         cms_entry               *entry = rem->data.e;
672         int                     size;
673
674         if ((rptr = (cms_reminder_ref *)calloc(1, sizeof(cms_reminder_ref)))
675             == NULL)
676                 return (NULL);
677
678         if ((rptr->reminder_name = strdup(entry->attrs[rem->rem.i].name.name))
679             == NULL) {
680                 free(rptr);
681                 return (NULL);
682         }
683
684         size = entry->attrs[CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
685                 opaque_data_value->size;
686
687         if ((rptr->entryid = malloc(size)) == NULL) {
688                 free(rptr->reminder_name);
689                 free(rptr);
690                 return (NULL);
691         } else
692                 strncpy(rptr->entryid, (char *)entry->attrs\
693                         [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value->item.\
694                         opaque_data_value->data, size);
695
696         rptr->key.id = entry->key.id;
697         rptr->key.time = starttime ? starttime : rem->starttime;
698
699         rptr->runtime = runtime ? runtime : rem->runtime;
700
701         return (rptr);
702 }
703
704 static void
705 _RemoveReminderFromQ(
706         _DtCmsRemQueue  *remq,
707         int             qindex,
708         cms_entry       *entry,
709         List_node       *lnode, /* zero for one time entries */
710         time_t          starttime,
711         boolean_t       delfwd)
712 {
713         _DtCmsRemInfo   *rptr, *rptr1 = NULL, *rptr2 = NULL;
714         time_t          lead;
715         time_t          tick;
716         boolean_t       do_old, do_new;
717         RepeatEventState *restate;
718
719         _csa_iso8601_to_duration(entry->attrs[remq->aindex[qindex]].value->\
720                 item.reminder_value->lead_time, &lead);
721
722         if (lnode == 0) {
723                 if (entry->key.time - lead < remq->cutoff)
724                         _RemoveReminderFromList(&remq->oldhead[qindex], entry,
725                                 starttime, delfwd);
726                 else
727                         _RemoveReminderFromList(&remq->active[qindex], entry,
728                                 starttime, delfwd);
729         } else {
730                 tick = ClosestTick(entry->key.time, entry->key.time, lnode->re,
731                         &restate);
732                 if (do_old = (tick - lead < remq->cutoff))
733                         rptr1 = _RemoveReminderFromList(&remq->oldhead[qindex],
734                                 entry, starttime, delfwd);
735
736                 if (do_new = (lnode->lasttick - lead >= remq->cutoff))
737                         rptr2 = _RemoveReminderFromList(&remq->active[qindex],
738                                 entry, starttime, delfwd);
739
740                 if (rptr = rptr1 ? rptr1 : rptr2) {
741                         if (do_old && do_new) {
742                                 /* need to clean up the other queue
743                                  * since add reminder will add to both
744                                  * queue
745                                  */
746                                 if (rptr1 == NULL)
747                                         _RemoveReminderFromList(
748                                                 &remq->oldhead[qindex], entry,
749                                                 0, B_FALSE);
750                                 else
751                                         _RemoveReminderFromList(
752                                                 &remq->active[qindex], entry,
753                                                 0, B_FALSE);
754                         }
755
756                         _DtCmsAddReminder4EntryToQ(remq, entry,
757                                 remq->aindex[qindex], rptr->lnode);
758
759                         if (rptr1) free(rptr1);
760                         if (rptr2) free(rptr2);
761                 }
762         }
763 }
764
765 static _DtCmsRemInfo *
766 _RemoveReminderFromList(
767         _DtCmsRemInfo   **qhead,
768         cms_entry       *entry,
769         time_t          starttime,
770         boolean_t       delfwd)
771 {
772         _DtCmsRemInfo   *rptr, *prev;
773
774         /* find reminder in list */
775         for (rptr = *qhead, prev = NULL; rptr != NULL;
776             prev = rptr, rptr = rptr->next) {
777                 if (rptr->data.e != entry ||
778                     (starttime > 0 && !delfwd && rptr->starttime != starttime)||
779                     (starttime > 0 && delfwd && rptr->starttime < starttime))
780                 {
781                         continue;
782                 }
783
784                 if (prev == NULL)
785                         *qhead = rptr->next;
786                 else
787                         prev->next = rptr->next;
788
789                 if (starttime == 0 || delfwd) {
790                         free(rptr);
791                         return (NULL);
792                 } else
793                         return (rptr);
794
795         }
796
797         return (NULL);
798 }
799
800 /*
801  * move all reminders in active queue whose runtime < cutoff
802  * to old queue
803  */
804 static void
805 _UpdateReminderQ(_DtCmsRemQueue *remq, int qindex)
806 {
807         _DtCmsRemInfo   *rptr, *nptr;
808         cms_entry       *entry;
809         time_t          lead, tick;
810         RepeatEventState *restate;
811
812         for (; (rptr = remq->active[qindex]) != NULL &&
813             rptr->runtime < remq->cutoff; ) {
814
815                 remq->active[qindex] = rptr->next;
816
817                 rptr->next = NULL;
818
819                 if (rptr->lnode == NULL)
820                         _InsertReminder(&remq->oldhead[qindex], rptr);
821                 else {
822                         lead = rptr->starttime - rptr->runtime;
823                         entry = rptr->data.e;
824                         tick = ClosestTick(entry->key.time, entry->key.time,
825                                 rptr->lnode->re, &restate);
826
827                         if (tick == rptr->starttime) {
828                                 /* add this to old queue */
829                                 _InsertReminder(&remq->oldhead[qindex], rptr);
830
831                                 /* make copy of rptr */
832                                 nptr = (_DtCmsRemInfo *)calloc(1,
833                                         sizeof(_DtCmsRemInfo));
834                                 *nptr = *rptr;
835                                 rptr = nptr;
836                         }
837
838                         if ((rptr->lasttick - lead < remq->cutoff) ||
839                             (tick = _GetNextActiveTick(entry,
840                             remq->cutoff + lead, rptr->lasttick,
841                             rptr->lnode->re)) <= 0)
842                         {
843                                 free(rptr);
844                         } else {
845                                 rptr->starttime = tick;
846                                 rptr->runtime = tick - lead; 
847                                 _InsertReminder(&remq->active[qindex], rptr);
848                         }
849                 } 
850         }
851 }
852
853 static long
854 _GetNextActiveTick(
855         cms_entry       *entry,
856         time_t          target,
857         time_t          lasttick,
858         RepeatEvent     *re)
859 {
860         RepeatEventState        *restate;
861         time_t                  tick;
862
863         for (tick = ClosestTick(target, entry->key.time, re, &restate);
864             tick <= lasttick;
865             tick = NextTick(tick, entry->key.time, re, restate))
866         {
867                 if (tick <= 0 || !_DtCmsInExceptionList(entry, tick))
868                         break;
869         }
870         return (tick);
871 }
872
873 /*
874  * The reminders found will be linked with the list
875  * contained in rf_f
876  */
877 static CSA_return_code
878 _GetNextRemindersFromList(
879         _DtCmsRemInfo           *rlist,
880         time_t                  giventime,
881         cms_reminder_ref        **rf_r)
882 {
883         cms_reminder_ref        *rptr, *head = NULL, *tail;
884         time_t                  tick;
885         int                     lead;
886
887         /* get from active queue */
888         for (; rlist != NULL; rlist = rlist->next) {
889
890                 if (giventime < rlist->runtime)
891                         break;
892                 else if (rlist->lnode) {
893
894                         /* check the next active tick */
895                         lead = rlist->starttime - rlist->runtime;
896                         tick = _GetNextActiveTick(rlist->data.e,
897                                 giventime + lead + 1, rlist->lasttick,
898                                 rlist->lnode->re);
899
900                         if (tick > 0 &&
901                             (head == NULL || (tick-lead <= head->runtime)))
902                         {
903                                 if ((rptr = _GetReminderRefFromInfo(rlist, tick,
904                                     tick-lead)) == NULL) {
905                                         if (head)
906                                                 _DtCmsFreeReminderRef(head);
907                                         return (CSA_E_INSUFFICIENT_MEMORY);
908                                 }
909
910                                 if (head == NULL)
911                                         head = rptr;
912                                 else if (head->runtime = rptr->runtime) {
913                                         rptr->next = head;
914                                         head = rptr;
915                                 } else {
916                                         _DtCmsFreeReminderRef(head);
917                                         head = rptr;
918                                 }
919                         }
920                 }
921         }
922
923         if (rlist) {
924                 if (head) {
925                         if (head->runtime > rlist->runtime) {
926                                 _DtCmsFreeReminderRef(head);
927                                 head = NULL;
928                         } else if (head->runtime < rlist->runtime)
929                                 goto _done;
930                 }
931
932                 /* now do lookup in the remaining list */
933
934                 while (rlist != NULL) {
935                         if (rptr = _GetReminderRefFromInfo(rlist, 0, 0)) {
936                                 rptr->next = head;
937                                 head = rptr;
938                         } else {
939                                 if (head) _DtCmsFreeReminderRef(head);
940                                 return (CSA_E_INSUFFICIENT_MEMORY);
941                         }
942
943                         if (rlist->next &&
944                             rlist->next->runtime == rlist->runtime)
945                                 rlist = rlist->next;
946                         else
947                                 break;
948                 }
949         }
950
951 _done:
952         /* find tail */
953         if (head) {
954                 for (tail = head; tail->next != NULL; tail = tail->next) ;
955                 tail->next = *rf_r;
956                 *rf_r = head;
957         }
958
959         return (CSA_SUCCESS);
960 }
961
962 static void
963 _DtCmsAddReminder4EntryToQ(
964         _DtCmsRemQueue  *remq,
965         cms_entry       *entry,
966         int             aindex,
967         List_node       *lnode)
968 {
969         _DtCmsRemInfo   *rptr, *rptr2;
970         int             i;
971
972         if ((rptr = _BuildReminder4Entry(entry, aindex, lnode, remq->cutoff,
973             &rptr2)) == NULL)
974                 return;
975
976         for (i = 0; i < remq->num_queues; i++) {
977                 if (remq->aindex[i] == aindex) {
978                         if (rptr->runtime >= remq->cutoff)
979                                 _InsertReminder(&remq->active[i], rptr);
980                         else
981                                 _InsertReminder(&remq->oldhead[i], rptr);
982
983                         if (rptr2)
984                                 _InsertReminder(&remq->active[i], rptr2);
985                         break;
986                 }
987         }
988
989         if (i == remq->num_queues) {
990                 /* expand the queue */
991         }
992 }
993