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