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