Initial import of the CDE 2.1.30 sources from the Open Group.
[oweals/cde.git] / cde / programs / dtcm / server / delete.c
1 /* $XConsortium: delete.c /main/4 1995/11/09 12:43:26 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 "delete.h"
25 #include "cm.h"
26 #include "tree.h"
27 #include "list.h"
28 #include "cmsdata.h"
29 #include "log.h"
30 #include "access.h"
31 #include "insert.h"
32 #include "v5ops.h"
33 #include "repeat.h"
34 #include "rerule.h"
35 #include "reutil.h"
36 #include "iso8601.h"
37 #include "attr.h"
38
39 /******************************************************************************
40  * forward declaration of static functions used within the file
41  ******************************************************************************/
42 static boolean_t _InSequence(List_node *node, time_t time);
43 static CSA_return_code _AddException(cms_attribute *attr, time_t time);
44 static CSA_return_code _AddEndDateToRule(cms_attribute *attr, RepeatEvent *re,
45                                         time_t time);
46 static void _TruncateExceptionDates(cms_entry *newe, time_t ltick);
47
48 /*****************************************************************************
49  * extern functions used in the library
50  *****************************************************************************/
51
52 extern CSA_return_code
53 _DtCmsDeleteEntry(
54         _DtCmsCalendar  *cal,
55         char            *sender,
56         uint            access,
57         cms_key         *key,
58         cms_entry       **entry_r)
59 {
60         CSA_return_code stat;
61         cms_entry       *entry;
62         List_node       *lnode = NULL;
63         Tree_node       *tnode;
64
65         if ((entry = (cms_entry *)rb_lookup(cal->tree, (caddr_t)key)) == NULL) {
66                 /* find entry in the repeating entry list */
67                 if ((lnode = hc_lookup_node(cal->list, (caddr_t)key)) == NULL)
68                         return (CSA_X_DT_E_ENTRY_NOT_FOUND);
69                 else
70                         entry = (cms_entry *)lnode->data;
71         }
72
73         if (entry == NULL)
74                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
75
76         if (sender && (stat = _DtCmsCheckChangeAccess(sender, access, entry))
77             != CSA_SUCCESS)
78                 return (stat);
79
80         if (lnode == NULL) {
81                 if ((tnode = rb_delete(cal->tree, (caddr_t)key)) != NULL) {
82                         _DtCmsObsoleteReminder4Entry(cal->remq, entry, NULL,
83                                 0, B_FALSE);
84                         free(tnode);
85                 } else
86                         return (CSA_X_DT_E_ENTRY_NOT_FOUND);
87         } else {
88                 _DtCmsObsoleteReminder4Entry(cal->remq, entry, lnode,
89                         0, B_FALSE);
90                 hc_delete_node(cal->list, lnode);
91                 free(lnode);
92         }
93
94
95         if (entry_r)
96                 *entry_r = entry;
97         else
98                 _DtCm_free_cms_entry(entry);
99
100         return (CSA_SUCCESS);
101 }
102
103 extern CSA_return_code
104 _DtCmsDeleteEntryAndLog(
105         _DtCmsCalendar  *cal,
106         char            *sender,
107         uint            access,
108         cms_key         *key,
109         cms_entry       **entry_r)
110 {
111         CSA_return_code stat;
112         cms_entry       *entry;
113
114         if ((stat = _DtCmsDeleteEntry(cal, sender, access, key, &entry))
115             == CSA_SUCCESS) {
116                 if ((stat = _DtCmsV5TransactLog(cal, entry, _DtCmsLogRemove))
117                     != CSA_SUCCESS) {
118                         (void)_DtCmsInsertEntry(cal, entry);
119                         _DtCm_free_cms_entry(entry);
120                 } else if (entry_r)
121                         *entry_r = entry;
122                 else
123                         _DtCm_free_cms_entry(entry);
124         }
125
126         return (stat);
127 }
128
129 extern CSA_return_code
130 _DtCmsDeleteInstancesAndLog(
131         _DtCmsCalendar  *cal,
132         char            *sender,
133         uint            access,
134         cms_key         *key,
135         int             scope,
136         cms_entry       **newe,
137         cms_entry       **olde)
138 {
139         CSA_return_code stat;
140         cms_entry       *entry, *nentry;
141         List_node       *lnode;
142         int             fsize, count;
143         uint            tmp_num;
144         cms_attribute   *tmp_attrs, *aptr;
145         boolean_t       delentry = B_FALSE;
146
147         if ((lnode = hc_lookup_node(cal->list, (caddr_t)key)) == NULL)
148                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
149
150         entry = (cms_entry *)lnode->data;
151
152         if ((stat = _DtCmsCheckChangeAccess(sender, access, entry))
153             != CSA_SUCCESS)
154                 return (stat);
155
156         if (_DtCmsInExceptionList(entry, key->time) ||
157             !_InSequence(lnode, key->time))
158                 return (CSA_X_DT_E_ENTRY_NOT_FOUND);
159
160         if ((stat = _DtCm_copy_cms_entry(entry, &nentry)) != CSA_SUCCESS)
161                 return (stat);
162
163         if (scope == CSA_SCOPE_ONE)
164                 stat = _AddException(
165                         &nentry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I],
166                         key->time);
167         else {
168                 /* check whether we are deleting from fst instance */
169                 if (key->time == nentry->key.time)
170                         delentry = B_TRUE;
171                 else {
172                         stat = _DtCmsAddEndDateToRule(&nentry->attrs\
173                                 [CSA_ENTRY_ATTR_RECURRENCE_RULE_I],
174                                 lnode->re, key->time - 1);
175                         lnode->re->re_end_date = key->time - 1;
176
177                         _TruncateExceptionDates(nentry, key->time);
178                 }
179         }
180
181         if (stat != CSA_SUCCESS) {
182                 _DtCm_free_cms_entry(nentry);
183                 return (stat);
184         }
185
186         if (!delentry) {
187                 if ((stat = _DtCmsSetLastUpdate(nentry)) != SUCCESS) {
188                         _DtCm_free_cms_entry(nentry);
189                         return (stat);
190                 }
191
192                 /* remove original entry from log */
193                 if ((stat = _DtCmsGetFileSize(cal->calendar, &fsize))
194                     != CSA_SUCCESS) {
195                         _DtCm_free_cms_entry(nentry);
196                         return (stat);
197                 }
198
199                 if ((stat = _DtCmsV5TransactLog(cal, entry, _DtCmsLogRemove))
200                     != CSA_SUCCESS) {
201                         _DtCm_free_cms_entry(nentry);
202                         return (stat);
203                 }
204
205                 aptr = &nentry->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I];
206                 count = CountEvents(nentry->key.time, lnode->re, (aptr->value ?
207                         aptr->value->item.date_time_list_value : NULL));
208         }
209
210         if (count == 0 || delentry) {
211                 /*
212                  * *** obsolete reminders
213                  */
214                 _DtCmsObsoleteReminder4Entry(cal->remq, entry, lnode,
215                         0, B_FALSE);
216
217                 hc_delete_node(cal->list, lnode);
218                 free(lnode);
219
220                 _DtCm_free_cms_entry(nentry);
221
222                 if (olde)
223                         *olde = entry;
224                 else
225                         _DtCm_free_cms_entry(entry);
226
227                 if (newe)
228                         *newe = NULL;
229
230         } else {
231                 /* add new entry in memory and log in file */
232
233                 /* update the count */
234                 if (count == 1) {
235
236                         _DtCmsConvertToOnetime(nentry, lnode->re);
237                         stat = _DtCmsRbToCsaStat(rb_insert(cal->tree,
238                             (caddr_t)nentry, (caddr_t)&(nentry->key)));
239
240                 } else if (count != RE_INFINITY) {
241                         nentry->attrs[CSA_ENTRY_ATTR_NUMBER_RECURRENCES_I].\
242                                 value->item.uint32_value = count;
243                 }
244
245                 /* the new entry should be copied when it's updated
246                  * with all the info
247                  */
248                 if (stat == CSA_SUCCESS && newe)
249                         stat = _DtCm_copy_cms_entry(nentry, newe);
250
251                 if (stat || (stat = _DtCmsV5TransactLog(cal, nentry,
252                     _DtCmsLogAdd)) != CSA_SUCCESS) {
253                         _DtCmsTruncateFile(cal->calendar, fsize);
254                         _DtCm_free_cms_entry(nentry);
255                         if (newe) free(*newe);
256                 } else {
257                         if (count == 1) {
258                                 _DtCmsObsoleteReminder4Entry(cal->remq, entry,
259                                         lnode, 0, B_FALSE);
260                                 hc_delete_node(cal->list, lnode);
261                                 free(lnode);
262                                 _DtCmsAddReminders4Entry(&cal->remq, nentry, NULL);
263                         } else {
264                                 /* need to do the swap since the original entry
265                                  * pointer is stored in the reminder info
266                                  */
267                                 tmp_num = entry->num_attrs;
268                                 tmp_attrs = entry->attrs;
269                                 entry->num_attrs = nentry->num_attrs;
270                                 entry->attrs = nentry->attrs;
271                                 nentry->num_attrs = tmp_num;
272                                 nentry->attrs = tmp_attrs;
273
274                                 _DtCmsObsoleteReminder4Entry(cal->remq, entry,
275                                         lnode, key->time,
276                                         (scope == CSA_SCOPE_ONE ? B_FALSE :
277                                         B_TRUE));
278
279                                 if (scope == CSA_SCOPE_FORWARD ||
280                                     key->time == lnode->lasttick) {
281                                         lnode->lasttick = LastTick(
282                                                                 entry->key.time,
283                                                                 lnode->re);
284                                 }
285                         }
286
287                         if (olde)
288                                 *olde = (count == 1) ? entry : nentry;
289                         else
290                                 _DtCm_free_cms_entry((count==1)?entry:nentry);
291                 }
292         }
293
294         return (stat);
295 }
296
297 /*****************************************************************************
298  * static functions used within the file
299  *****************************************************************************/
300
301 static boolean_t
302 _InSequence(List_node *node, time_t time)
303 {
304         time_t          tick;
305         cms_entry       *entry = (cms_entry *)node->data;
306         RepeatEventState *restate;
307
308         for (tick = ClosestTick(time, entry->key.time, node->re, &restate);
309             tick <= node->lasttick;
310             tick = NextTick(tick, entry->key.time, node->re, restate))
311         {
312                 if (tick <= 0 || tick > node->lasttick)
313                         break;
314
315                 if (tick == time)
316                         return (B_TRUE);
317         }
318
319         return (B_FALSE);
320 }
321
322 static CSA_return_code
323 _AddException(cms_attribute *attr, time_t time)
324 {
325         CSA_date_time_entry     *dt, *dlist, *prev;
326         cms_attribute_value     *val;
327         time_t                  tick;
328         char                    buf[20];
329
330         if ((dt = (CSA_date_time_entry *)calloc(1, sizeof(CSA_date_time_entry)))
331             == NULL)
332                 return (CSA_E_INSUFFICIENT_MEMORY);
333
334         if (_csa_tick_to_iso8601(time, buf)) {
335                 free(dt);
336                 return (CSA_E_INVALID_DATE_TIME);
337         } else if ((dt->date_time = strdup(buf)) == NULL) {
338                 free(dt);
339                 return (CSA_E_INSUFFICIENT_MEMORY);
340         }
341
342         if (attr->value == NULL) {
343                 if ((val = (cms_attribute_value *)calloc(1,
344                     sizeof(cms_attribute_value))) == NULL) {
345                         free(dt->date_time);
346                         free(dt);
347                         return (CSA_E_INSUFFICIENT_MEMORY);
348                 }
349                 attr->value = val;
350                 val->type = CSA_VALUE_DATE_TIME_LIST;
351         }
352
353         if (attr->value->item.date_time_list_value == NULL) {
354                 val->item.date_time_list_value = dt;
355         } else {
356                 for (dlist = attr->value->item.date_time_list_value, prev=NULL;
357                     dlist != NULL;
358                     prev = dlist, dlist = dlist->next) {
359                         _csa_iso8601_to_tick(dlist->date_time, &tick);
360                         if (time <= tick)
361                                 break;
362                 }
363
364                 dt->next = dlist;
365                 if (prev == NULL)
366                         attr->value->item.date_time_list_value = dt;
367                 else
368                         prev->next = dt;
369         }
370
371         return (CSA_SUCCESS);
372 }
373
374 static void
375 _TruncateExceptionDates(cms_entry *newe, time_t ltick)
376 {
377         time_t                  tick;
378         CSA_date_time_list      dt, prev, head;
379
380         if (newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value == NULL ||
381             newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
382             date_time_list_value == NULL)
383                 return;
384
385         head = newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].value->item.\
386                 date_time_list_value;
387
388         for (dt = head, prev = NULL; dt != NULL; prev = dt, dt = dt->next) {
389                 _csa_iso8601_to_tick(dt->date_time, &tick);
390                 if (ltick < tick) {
391                         if (prev) {
392                                 prev->next = NULL;
393                                 _DtCm_free_date_time_list(dt);
394                         } else {
395                                 free(newe->attrs\
396                                         [CSA_ENTRY_ATTR_EXCEPTION_DATES_I].\
397                                         value);
398                                 newe->attrs[CSA_ENTRY_ATTR_EXCEPTION_DATES_I].\
399                                         value = NULL;
400                         }
401                         break;
402                 }
403         }
404 }
405