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