Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / update.c
1 /* $XConsortium: update.c /main/4 1995/11/09 12:53:47 rswiston $ */
2 /*
3  *  (c) Copyright 1993, 1994 Hewlett-Packard Company
4  *  (c) Copyright 1993, 1994 International Business Machines Corp.
5  *  (c) Copyright 1993, 1994 Novell, Inc.
6  *  (c) Copyright 1993, 1994 Sun Microsystems, Inc.
7  */
8
9 #include <EUSCompat.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <signal.h>
14 #include <sys/stat.h>
15 #include <errno.h>
16 #include <string.h>
17 #include <pwd.h>
18 #include <time.h>
19 #include <values.h>
20 #ifdef SunOS
21 #include <sys/systeminfo.h>
22 #endif
23 #include "cmscalendar.h"
24 #include "update.h"
25 #include "cm.h"
26 #include "attr.h"
27 #include "updateattrs.h"
28 #include "cmsdata.h"
29 #include "cmsentry.h"
30 #include "access.h"
31 #include "repeat.h"
32 #include "delete.h"
33 #include "insert.h"
34 #include "log.h"
35 #include "v5ops.h"
36 #include "iso8601.h"
37 #include "rerule.h"
38 #include "reutil.h"
39
40 extern char *_DtCm_rule_buf;            /* buffer to hold a rule for parser */
41 extern RepeatEvent *_DtCm_repeat_info;  /* parsed recurrence info */
42
43 extern boolean_t RulesMatch(char *rule1, char *rule2);
44
45 /******************************************************************************
46  * forward declaration of static functions used within the file
47  ******************************************************************************/
48 static boolean_t _SameRecurrenceRule(
49                         cms_attribute_value *newval,
50                         cms_attribute_value *oldval);
51
52 static CSA_return_code _SetNewStartDate(
53                         cms_entry       *olde,
54                         RepeatEvent     *oldre,
55                         cms_entry       *newe,
56                         RepeatEvent     *newre,
57                         cms_key         *key);
58
59 static CSA_return_code _AdjustStartEndTimeForUpdateInst(
60                         cms_entry       *newe,
61                         cms_entry       *olde,
62                         cms_key         *key,
63                         uint            num_attrs,
64                         cms_attribute   *attrs);
65
66 static CSA_return_code _AdjustStartEndTimeForUpdateEntry(
67                         List_node       *lnode,
68                         cms_entry       *newe,
69                         cms_key         *key,
70                         uint            num_attrs,
71                         cms_attribute   *attrs);
72
73 static void _GetStartEndIndex(
74                         uint            num_attrs,
75                         cms_attribute   *attrs,
76                         int             *starti,
77                         int             *endi);
78
79 static void _AdjustExceptionDates(cms_entry *entry, time_t delta);
80
81 static int _NumberExceptionDates(cms_entry *entry);
82
83 /*****************************************************************************
84  * extern functions used in the library
85  *****************************************************************************/
86
87 extern CSA_return_code
88 _DtCmsUpdateEntry(
89         _DtCmsCalendar  *cal,
90         char            *sender,
91         uint            access,
92         cms_key         *key,
93         uint            num_attrs,
94         cms_attribute   *attrs,
95         cms_entry       **oldentry,
96         cms_entry       **newentry)
97 {
98         CSA_return_code stat;
99         cms_entry       *olde, *newe;
100         List_node       *lnode = NULL;
101         int             file_size;
102
103         /* get the entry from the tree */
104         if ((olde = (cms_entry *)rb_lookup(cal->tree, (caddr_t)key)) == NULL) {
105                 /* find entry in the repeating entry list */
106                 if ((lnode = hc_lookup_node(cal->list, (caddr_t)key)) == NULL)
107                         return (CSA_X_DT_E_ENTRY_NOT_FOUND);
108                 else
109                         olde = (cms_entry *)lnode->data;
110         }
111
112         if (olde == NULL)
113                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
114
115         /* check access rights */
116         if ((stat = _DtCmsCheckChangeAccess(sender, access, olde))
117             != CSA_SUCCESS)
118                 return (stat);
119
120         /* copy the entry and apply updates */
121         if ((stat = _DtCm_copy_cms_entry(olde, &newe)) != CSA_SUCCESS)
122                 return (stat);
123
124         if ((stat = _DtCmUpdateAttributes(num_attrs, attrs, &newe->num_attrs,
125             &newe->attrs, &cal->entry_tbl, B_FALSE, &cal->types, B_FALSE))
126             != CSA_SUCCESS) {
127                 _DtCm_free_cms_entry(newe);
128                 return (stat);
129         }
130
131         /* update start date */
132         _csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].value->\
133                 item.date_time_value, &newe->key.time);
134
135         if (lnode != NULL && (stat = _AdjustStartEndTimeForUpdateEntry(
136             lnode, newe, key, num_attrs, attrs)) != CSA_SUCCESS) {
137                 _DtCm_free_cms_entry(newe);
138                 return (stat);
139         }
140
141         /* make sure end time is not earlier than start time */
142         if ((stat = _DtCmsCheckStartEndTime(newe)) != CSA_SUCCESS) {
143                 _DtCm_free_cms_entry(newe);
144                 return (stat);
145         }
146
147         /* set last update */
148         if ((stat = _DtCmsSetLastUpdate(newe)) != CSA_SUCCESS) {
149                 _DtCm_free_cms_entry(newe);
150                 return (stat);
151         }
152
153         /* save file size in case we need to roll back */
154         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
155             != CSA_SUCCESS) {
156                 _DtCm_free_cms_entry(newe);
157                 return (stat);
158         }
159
160         /* remove old entry */
161         if ((stat = _DtCmsDeleteEntryAndLog(cal, NULL, 0, key, &olde))
162             != CSA_SUCCESS){
163                 _DtCm_free_cms_entry(newe);
164                 return (stat);
165         }
166
167         /* insert new entry */
168         if ((stat = _DtCmsInsertEntryAndLog(cal, newe)) != CSA_SUCCESS) {
169
170                 _DtCmsTruncateFile(cal->calendar, file_size);
171                 _DtCmsInsertEntry(cal, olde);
172                 _DtCm_free_cms_entry(newe);
173                 _DtCm_free_cms_entry(olde);
174
175         } else {
176                  if (newentry)
177                         *newentry = newe;
178                 else
179                         _DtCm_free_cms_entry(newe);
180
181                 if (oldentry)
182                         *oldentry = olde;
183                 else
184                         _DtCm_free_cms_entry(olde);
185         }
186
187         return (stat);
188 }
189
190 extern CSA_return_code
191 _DtCmsUpdateInstances(
192         _DtCmsCalendar  *cal,
193         char            *sender,
194         uint            access,
195         cms_key         *key,
196         int             scope,
197         uint            num_attrs,
198         cms_attribute   *attrs,
199         cms_entry       **oldentry,
200         cms_entry       **newentry)
201 {
202         CSA_return_code stat;
203         cms_entry       *olde, *newe = NULL, *updatedold;
204         List_node       *lnode = NULL;
205         int             file_size;
206         int             i, remain, rulei;
207         cms_attribute_value *oaptr, *naptr;
208
209         /* save file size in case we need to roll back */
210         if ((stat = _DtCmsGetFileSize(cal->calendar, &file_size))
211             != CSA_SUCCESS)
212                 return (stat);
213
214         /* remove old entry */
215         if ((stat = _DtCmsDeleteInstancesAndLog(cal, sender, access, key, scope,
216             &updatedold, &olde))
217             != CSA_SUCCESS)
218                 return (stat);
219
220         /* copy the entry and apply updates */
221         if ((stat = _DtCm_copy_cms_entry(olde, &newe)) != CSA_SUCCESS)
222                 goto _cleanup;
223
224         if ((stat = _DtCmUpdateAttributes(num_attrs, attrs, &newe->num_attrs,
225             &newe->attrs, &cal->entry_tbl, B_FALSE, &cal->types, B_FALSE))
226             != CSA_SUCCESS)
227                 goto _cleanup;
228
229         /* check recurrence rule */
230         for (i = 0, rulei = -1; i < num_attrs; i++) {
231                 if (attrs[i].name.num == CSA_ENTRY_ATTR_RECURRENCE_RULE_I) {
232                         rulei = i;
233                         break;
234                 }
235         }
236
237         if (scope == CSA_SCOPE_ONE) {
238                 if (rulei == -1) {
239                         _DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
240                                 [CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].value);
241                         _DtCmUpdateStringAttrVal(NULL, &newe->attrs\
242                                 [CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value);
243                         _DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
244                                 [CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I].value);
245                         _DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
246                                 [CSA_X_DT_ENTRY_ATTR_REPEAT_TIMES_I].value);
247                         _DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
248                                 [CSA_X_DT_ENTRY_ATTR_REPEAT_INTERVAL_I].value);
249                         _DtCmUpdateSint32AttrVal(NULL, &newe->attrs\
250                                 [CSA_X_DT_ENTRY_ATTR_REPEAT_OCCURRENCE_NUM_I].\
251                                 value);
252                         _DtCmUpdateStringAttrVal(NULL, &newe->attrs\
253                                 [CSA_X_DT_ENTRY_ATTR_SEQUENCE_END_DATE_I].value);
254                 }
255         } else {
256                 if (rulei == -1 ||
257                     _SameRecurrenceRule(attrs[rulei].value, olde->\
258                     attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value)) {
259
260                         /*
261                          * if recurrence info is not changed, replace
262                          * the deleted part with the new one, i.e.,
263                          * duration of new equals to the number of
264                          * deleted instances of the old one.
265                          * Also, update the exception list.
266                          */
267
268                         if (newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value)
269                         {
270                                 _DtCmsCleanupExceptionDates(newe, key->time);
271                         }
272
273                         oaptr = olde->attrs\
274                                 [CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].value;
275
276                         naptr = (updatedold == NULL) ? NULL :
277                                 updatedold->attrs[\
278                                 CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].\
279                                 value;
280
281                         if (oaptr->item.uint32_value == 0)
282                                 remain = 0;
283                         else
284                                 remain = oaptr->item.uint32_value -
285                                         (naptr ? naptr->item.uint32_value : 0) +
286                                         _DtCmsNumberExceptionDates(newe);
287
288                         if ((stat = _DtCmsUpdateDurationInRule(newe, remain))
289                             != CSA_SUCCESS)
290                                 goto _cleanup;
291                 }
292         }
293
294         if ((stat = _AdjustStartEndTimeForUpdateInst(newe, olde, key,
295             num_attrs, attrs)) != CSA_SUCCESS)
296                 goto _cleanup;
297
298         /* set last update */
299         if ((stat = _DtCmsSetLastUpdate(newe)) != CSA_SUCCESS)
300                 goto _cleanup;
301
302         /* insert new entry */
303         newe->key.id = 0;
304         if ((stat = _DtCmsInsertEntryAndLog(cal, newe)) != CSA_SUCCESS) {
305
306                 goto _cleanup;
307
308         }
309
310         if (newentry)
311                 *newentry = newe;
312         else
313                 _DtCm_free_cms_entry(newe);
314
315         if (oldentry)
316                 *oldentry = olde;
317         else
318                 _DtCm_free_cms_entry(olde);
319
320         if (updatedold) _DtCm_free_cms_entry(updatedold);
321
322         return (stat);
323
324 _cleanup:
325         _DtCmsTruncateFile(cal->calendar, file_size);
326         if (updatedold == NULL)
327                 _DtCmsInsertEntry(cal, olde);
328         else {
329                 _DtCm_free_cms_entry(updatedold);
330                 if (lnode = hc_lookup_node(cal->list, (caddr_t)key)) {
331                         updatedold = (cms_entry *)lnode->data;
332                         lnode->data = (caddr_t)olde;
333                         olde = updatedold;
334                 }
335         }
336         _DtCm_free_cms_entry(olde);
337
338         if (newe) _DtCm_free_cms_entry(newe);
339
340         return (stat);
341 }
342
343 /*****************************************************************************
344  * static functions used within the file
345  *****************************************************************************/
346
347 static boolean_t
348 _SameRecurrenceRule(cms_attribute_value *newval, cms_attribute_value *oldval)
349 {
350         if (newval == NULL || newval->item.string_value == NULL)
351                 return (B_FALSE);
352
353         if (strcmp(newval->item.string_value, oldval->item.string_value))
354                 return (B_FALSE);
355         else
356                 return (B_TRUE);
357 }
358
359 static CSA_return_code
360 _AdjustStartEndTimeForUpdateInst(
361         cms_entry       *newe,
362         cms_entry       *olde,
363         cms_key         *key,
364         uint            num_attrs,
365         cms_attribute   *attrs)
366 {
367         CSA_return_code stat;
368         time_t          oldbod, newbod, endtime, delta;
369         int             i, starti, endi;
370
371         /* update start date */
372         _GetStartEndIndex(num_attrs, attrs, &starti, &endi);
373
374         oldbod = _DtCmsBeginOfDay(olde->key.time);
375         if (starti >= 0 && endi == -1 &&
376             newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
377
378                 /* adjust end date */
379
380                 _csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
381                         value->item.date_time_value, &newe->key.time);
382                 newbod = _DtCmsBeginOfDay(newe->key.time);
383
384                 _csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].\
385                         value->item.date_time_value, &endtime);
386                 endtime += (newbod - oldbod);
387                 _csa_tick_to_iso8601(endtime, newe->attrs\
388                         [CSA_ENTRY_ATTR_END_DATE_I].value->item.\
389                         date_time_value);
390
391         } else if (starti == -1 && endi >= 0) {
392                 /* ajust start date */
393                 if (newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
394                         _csa_iso8601_to_tick(newe->attrs\
395                                 [CSA_ENTRY_ATTR_END_DATE_I].value->\
396                                 item.date_time_value, &endtime);
397                         newbod = _DtCmsBeginOfDay(endtime);
398                 } else
399                         newbod = _DtCmsBeginOfDay(key->time);
400
401                 newe->key.time += (newbod - oldbod);
402                 _csa_tick_to_iso8601(newe->key.time, newe->attrs\
403                         [CSA_ENTRY_ATTR_START_DATE_I].value->item.\
404                         date_time_value);
405
406         } else if (starti == -1 && endi == -1) {
407                 /* adjust both start and end date */
408
409                 newe->key.time = key->time;
410                 _csa_tick_to_iso8601(key->time, newe->attrs[\
411                         CSA_ENTRY_ATTR_START_DATE_I].value->item.\
412                         date_time_value);
413
414                 if (newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value) {
415                         newbod = _DtCmsBeginOfDay(newe->key.time);
416
417                         _csa_iso8601_to_tick(newe->attrs\
418                                 [CSA_ENTRY_ATTR_END_DATE_I].value->\
419                                 item.date_time_value, &endtime);
420                         endtime += (newbod - oldbod);
421                         _csa_tick_to_iso8601(endtime, newe->attrs\
422                                 [CSA_ENTRY_ATTR_END_DATE_I].value->\
423                                 item.date_time_value);
424                 }
425         } else {
426                 _csa_iso8601_to_tick(newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
427                         value->item.date_time_value, &newe->key.time);
428         }
429
430         if ((stat = _DtCmsCheckStartEndTime(newe)) != CSA_SUCCESS) {
431                 return (stat);
432         }
433
434         if ((delta = _DtCmsTimeOfDay(newe->key.time) -
435             _DtCmsTimeOfDay(key->time)) != 0)
436                 _AdjustExceptionDates(newe, delta);
437
438         return (CSA_SUCCESS);
439 }
440
441 static CSA_return_code
442 _SetNewStartDate(
443         cms_entry       *olde,
444         RepeatEvent     *oldre,
445         cms_entry       *newe,
446         RepeatEvent     *newre,
447         cms_key         *key)
448 {
449         if ((newe->key.time = DeriveNewStartTime(olde->key.time, oldre,
450             key->time, newe->key.time, newre)) == 0)
451                 return (CSA_E_FAILURE);
452
453         if (_csa_tick_to_iso8601(newe->key.time, newe->attrs\
454             [CSA_ENTRY_ATTR_START_DATE_I].value->item.date_time_value))
455                 return (CSA_E_FAILURE);
456
457         return (CSA_SUCCESS);
458 }
459
460 /*
461  * The assumption is that there's only one instance per day.
462  * When we support more than one instance per day, this
463  * needs to be updated.
464  */
465 static void
466 _AdjustExceptionDates(cms_entry *entry, time_t delta)
467 {
468         time_t                  tick;
469         CSA_date_time_list      dt, head;
470
471         if (entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value == NULL ||
472             entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
473             date_time_list_value == NULL)
474                 return;
475
476         head = entry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
477                 date_time_list_value;
478
479         for (dt = head; dt != NULL; dt = dt->next) {
480                 _csa_iso8601_to_tick(dt->date_time, &tick);
481                 tick += delta;
482                 _csa_tick_to_iso8601(tick, dt->date_time);
483         }
484 }
485
486 static void
487 _GetStartEndIndex(
488         uint            num_attrs,
489         cms_attribute   *attrs,
490         int             *starti,
491         int             *endi)
492 {
493         int     i;
494
495         for (i = 0, *starti = -1, *endi = -1; i < num_attrs; i++) {
496                 if (attrs[i].name.num == CSA_ENTRY_ATTR_START_DATE_I)
497                         *starti = i;
498                 else if (attrs[i].name.num == CSA_ENTRY_ATTR_END_DATE_I)
499                         *endi = i;
500         }
501 }
502
503 static CSA_return_code
504 _AdjustStartEndTimeForUpdateEntry(
505         List_node       *lnode,
506         cms_entry       *newe,
507         cms_key         *key,
508         uint            num_attrs,
509         cms_attribute   *attrs)
510 {
511         CSA_return_code stat;
512         cms_entry       *olde = (cms_entry *)lnode->data;
513         time_t          newbod, instbod, fstbod, newfstbod, endtime;
514         int             starti, endi, enddelta = 0;
515         cms_attribute_value *oldaptr, *newaptr, *endaptr;
516         RepeatEvent     *newre;
517
518 extern  void _DtCm_rule_parser();
519
520         /* check to make sure repeating type is not changed */
521         oldaptr = olde->attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value;
522         newaptr = newe->attrs[CSA_ENTRY_ATTR_RECURRENCE_RULE_I].value;
523         endaptr = newe->attrs[CSA_ENTRY_ATTR_END_DATE_I].value;
524         if (endaptr)
525                 _csa_iso8601_to_tick(endaptr->item.date_time_value, &endtime);
526
527         if (key->time != olde->key.time &&
528             (newaptr == NULL || newaptr->item.string_value == NULL ||
529             RulesMatch(oldaptr->item.string_value, newaptr->item.string_value)
530             == B_FALSE)) {
531                 return (CSA_E_INVALID_ATTRIBUTE_VALUE);
532         }
533
534         _GetStartEndIndex(num_attrs, attrs, &starti, &endi);
535         newbod = _DtCmsBeginOfDay(newe->key.time);
536         instbod = _DtCmsBeginOfDay(key->time);
537         fstbod = _DtCmsBeginOfDay(olde->key.time);
538
539         if (starti >= 0 && key->time != olde->key.time) {
540
541                 if (newbod == instbod) {
542                         /* keep the same start day */
543                         newe->key.time -= (newbod - fstbod); 
544                         _csa_tick_to_iso8601(newe->key.time,
545                                 newe->attrs[CSA_ENTRY_ATTR_START_DATE_I].\
546                                 value->item.date_time_value);
547
548                         if (endi >= 0)
549                                 enddelta = newbod - fstbod;
550                 } else {
551                         /* parse the rule */
552                         _DtCm_rule_buf = newaptr->item.string_value;
553                         _DtCm_rule_parser();
554                         if ((newre = _DtCm_repeat_info) == NULL)
555                                 return (CSA_E_INVALID_RULE);
556
557                         /* get new start date */
558                         if ((stat = _SetNewStartDate(olde, lnode->re,
559                             newe, newre, key)) != CSA_SUCCESS) {
560                                 _DtCm_free_re(newre);
561                                 return (stat); 
562                         }
563                         _DtCm_free_re(newre);
564                         newfstbod = _DtCmsBeginOfDay(newe->key.time);
565                         if (endi < 0)
566                                 enddelta = fstbod - newfstbod;
567                         else
568                                 enddelta = newbod - newfstbod;
569                 }
570         } else if (starti >= 0 && endi < 0 && newbod != fstbod) {
571
572                 enddelta = fstbod - newbod;
573
574         } else if (starti < 0 && endi >= 0 && key->time != olde->key.time &&
575                 endaptr)
576         {
577                 enddelta = _DtCmsBeginOfDay(endtime) - fstbod;
578         }
579
580         /* fix end date */
581         if (enddelta && endaptr) {
582
583                 endtime -= enddelta;
584                 _csa_tick_to_iso8601(endtime, endaptr->item.date_time_value);
585         }
586
587         return (CSA_SUCCESS);
588 }
589