Add GNU LGPL headers to all .c .C and .h files
[oweals/cde.git] / cde / programs / dtcm / server / rtable4.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: rtable4.c /main/5 1996/10/02 17:31:51 drk $ */
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 /*
32  * version 4 of calendar manager rpc protocol functions.
33  */
34
35 #include <EUSCompat.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 #include <errno.h>
40 #include <time.h>
41 #include <sys/stat.h>
42 #include <fcntl.h>
43 #include "rtable4.h"
44 #include <sys/param.h>
45 #include <sys/time.h>
46 #include <sys/signal.h>
47 #include <rpc/rpc.h>
48 #include <values.h>
49 #include <string.h>
50 #include <pwd.h>
51 #ifdef SUNOS
52 #include <netdir.h>
53 #else
54 #include <sys/socket.h>
55 #include <netdb.h>
56 #endif
57 #include "cm.h"
58 #include "access.h"
59 #include "laccess.h"
60 #include "callback.h"
61 #include "appt4.h"              
62 #include "log.h"
63 #include "tree.h"
64 #include "list.h"
65 #include "cmscalendar.h"
66 #include "v4ops.h"
67 #include "v5ops.h"
68 #include "reminder.h"
69 #include "repeat.h"
70 #include "utility.h"
71 #include "lutil.h"
72 #include "rpcextras.h"
73 #include "rtable4_tbl.i"
74 #include "lookup.h"
75 #include "cmsdata.h"
76 #include "attr.h"
77 #include "convert5-4.h"
78 #include "convert4-5.h"
79 #include "cmsconvert.h"
80 #include "misc.h"
81 #include "insert.h"
82 #include "delete.h"
83 #include "update.h"
84
85
86 extern  int     debug;
87 extern  char    *pgname;
88
89 /*****************************************************************************
90  * forward declaration of static functions used within the file
91  *****************************************************************************/
92
93 static Appt_4 * rtable_lookup_internal(_DtCmsCalendar *cal, char **p_src,
94                         Id_4 *key);
95
96 static Access_Status_4 csastat2accessstat(CSA_return_code stat);
97
98 static Registration_Status_4 csastat2regstat(CSA_return_code stat);
99
100 static Table_Status_4 csastat2tablestat(CSA_return_code stat);
101
102 static Table_Res_4 * table_lookup_next(Table_Args_4 *args,
103                         struct svc_req *svcrq, caddr_t (* rb_func)(),
104                         Appt_4 *(* rp_func)());
105
106 static Appt_4 * repeater_next_smaller(List_node *p_lnode, Id_4 *key);
107
108 static Appt_4 * repeater_next_larger(List_node *p_lnode, Id_4 *key);
109
110 /*****************************************************************************
111  * extern functions used in the library
112  *****************************************************************************/
113
114 /*
115  * supports both data format
116  */
117 extern Table_Res_4 *
118 _DtCm_rtable_lookup_4_svc (Table_Args_4 *args, struct svc_req *svcrq)
119 {
120         static Table_Res_4 res;
121         CSA_return_code         stat;
122         Appt_4          *p_appt;
123         Appt_4          *h = NULL;
124         _DtCmsCalendar  *cal;
125         Uid_4           *p_keys;
126         Id_4            *key;
127         char            *user;
128         uint            access;
129         cms_entry       *entries;
130         cms_key         cmskey;
131         time_t          tmptick = 0;
132
133         if (debug)
134                 fprintf(stderr, "_DtCm_rtable_lookup_4_svc called\n");
135
136         if (res.res.Table_Res_List_4_u.a)
137                 _DtCm_free_appt4(res.res.Table_Res_List_4_u.a);
138
139         res.status = access_other_4;
140         res.res.tag = AP_4;
141         res.res.Table_Res_List_4_u.a = NULL;
142         if ((p_keys = args->args.Args_4_u.key) == NULL)
143                 return (&res);
144
145         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
146             &access, &cal)) == CSA_SUCCESS) {
147                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
148                         if (_DTCMS_HAS_V4_BROWSE_ACCESS(access))
149                                 res.status = access_ok_4;
150                         else
151                                 res.status = access_failed_4;
152                 } else if (!_DTCMS_HAS_VIEW_ACCESS(access)) {
153                         res.status = access_failed_4;
154                         return (&res);
155                 } else
156                         res.status = access_ok_4;
157         } else {
158                 res.status = csastat2accessstat(stat);
159                 return (&res);
160         }
161
162         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
163                 while (p_keys != NULL) {
164                         cmskey.time = p_keys->appt_id.tick;
165                         cmskey.id = p_keys->appt_id.key;
166
167                         if ((stat = _DtCmsGetEntryAttrByKey(cal, user, access,
168                             cmskey, 0, NULL, &entries, NULL))
169                             == CSA_SUCCESS) {
170
171                                 if ((stat = _DtCmsCmsentriesToAppt4ForClient(
172                                     entries, &p_appt)) == CSA_SUCCESS) {
173                                         /* link to appt list */
174                                         h = _AddApptInOrder(h, p_appt);
175                                 }
176                                 _DtCm_free_cms_entries(entries);
177                         }
178
179                         if (stat != CSA_SUCCESS) {
180                                 res.status = csastat2accessstat(stat);
181                                 if (h) {
182                                         _DtCm_free_appt4(h);
183                                         h = NULL;
184                                 }
185                                 break;
186                         }
187                         p_keys = p_keys->next;
188                 }
189
190                 res.res.Table_Res_List_4_u.a = h;
191                 return (&res);
192         }
193
194         /* do lookup on old format calendar */
195         while (p_keys != NULL)
196         {
197                 key = &p_keys->appt_id;
198
199                 if (debug) {
200                         fprintf(stderr,
201                             "_DtCm_rtable_lookup_4_svc at (key %ld)%s\n",
202                             key->key, ctime(&key->tick));
203                 }
204
205                 p_appt = (Appt_4 *)rb_lookup(APPT_TREE(cal), (caddr_t)key);
206                 if (p_appt == NULL) {
207                         if ((p_appt = (Appt_4 *)hc_lookup(REPT_LIST(cal),
208                             (caddr_t)key)) != NULL) {
209                                 if (!_DtCms_in_repeater(key, p_appt, B_FALSE))
210                                         p_appt = NULL;
211                         }
212                 }
213
214                 if (p_appt != NULL) {
215                         if (p_appt->appt_id.tick != key->tick) {
216                                 tmptick = p_appt->appt_id.tick;
217                                 p_appt->appt_id.tick = key->tick;
218                         }
219
220                         stat = _AddToLinkedAppts(p_appt, user, access,
221                                         (caddr_t *)&h);
222
223                         if (tmptick) p_appt->appt_id.tick = tmptick;
224
225                         if (stat != CSA_SUCCESS) {
226                                 if (h) {
227                                         _DtCm_free_appt4(h);
228                                         h = NULL;
229                                 }
230                                 res.status = csastat2accessstat(stat);
231                         }
232                         break;
233                 }
234
235                 p_keys = p_keys->next;
236         }
237
238         res.res.Table_Res_List_4_u.a = h;
239         return (&res);
240 }
241
242 /*
243  * supports old data format only
244  */
245 extern Table_Res_4 *
246 _DtCm_rtable_lookup_next_larger_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
247 {
248         Table_Res_4 *res;
249
250         if (debug)
251                 fprintf(stderr, "_DtCm_rtable_lookup_next_larger_4_svc called\n");
252
253         res = table_lookup_next(args, svcrq, rb_lookup_next_larger,
254                                 repeater_next_larger);
255         return (res);
256 }
257
258 /*
259  * supports old data format only
260  */
261 extern Table_Res_4 *
262 _DtCm_rtable_lookup_next_smaller_4_svc(
263         Table_Args_4    *args,
264         struct svc_req  *svcrq)
265 {
266         Table_Res_4 *res;
267
268         if (debug)
269                 fprintf(stderr, "_DtCm_rtable_lookup_next_smaller_4_svc called\n");
270
271         res = table_lookup_next(args, svcrq, rb_lookup_next_smaller,
272                                 repeater_next_smaller);
273         return (res);
274 }
275
276 /*
277  * supports both data format
278  */
279 extern Table_Res_4 *
280 _DtCm_rtable_lookup_range_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
281 {
282         static Table_Res_4 res;
283         CSA_return_code stat;
284         _DtCmsCalendar  *cal;
285         char            *user;
286         uint            access;
287
288         if (debug)
289                 fprintf(stderr, "_DtCm_rtable_lookup_range_4_svc called\n");
290
291         if (res.res.Table_Res_List_4_u.a)
292                 _DtCm_free_appt4(res.res.Table_Res_List_4_u.a);
293
294         res.res.tag = AP_4;
295         res.res.Table_Res_List_4_u.a = NULL;
296
297         res.status = access_other_4;
298         if (args->args.Args_4_u.range == NULL)
299                 return (&res);
300
301         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
302             &access, &cal)) == CSA_SUCCESS) {
303                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
304                         if (_DTCMS_HAS_V4_BROWSE_ACCESS(access))
305                                 res.status = access_ok_4;
306                         else
307                                 res.status = access_failed_4;
308                 }
309         } else {
310                 res.status = csastat2accessstat(stat);
311                 return (&res);
312         }
313
314         stat = _DtCmsLookupRangeV4(cal, user, access, args->args.Args_4_u.range,
315                 B_TRUE, 0, 0, NULL, 0, NULL, NULL,
316                 &res.res.Table_Res_List_4_u.a, NULL);
317
318         res.status = csastat2accessstat(stat);
319         return (&res);
320 }
321
322 /*
323  * supports both data format
324  */
325 extern Table_Res_4 *
326 _DtCm_rtable_abbreviated_lookup_range_4_svc(
327         Table_Args_4    *args,
328         struct svc_req  *svcrq)
329 {
330         static Table_Res_4 res;
331         CSA_return_code stat;
332         _DtCmsCalendar  *cal;
333         char            *user;
334         uint            access;
335
336         if (debug)
337                 fprintf(stderr,
338                         "_DtCm_rtable_abbreviated_lookup_range_4_svc called\n");
339
340         if (res.res.Table_Res_List_4_u.b)
341                 _DtCm_free_abbrev_appt4(res.res.Table_Res_List_4_u.b);
342
343         res.res.tag = AB_4;
344         res.res.Table_Res_List_4_u.b = NULL;
345
346         res.status = access_other_4;
347         if (args->args.Args_4_u.range == NULL)
348                 return (&res);
349
350         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
351             &access, &cal)) == CSA_SUCCESS) {
352                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
353                         if (_DTCMS_HAS_V4_BROWSE_ACCESS(access))
354                                 res.status = access_ok_4;
355                         else
356                                 res.status = access_failed_4;
357                 }
358         } else {
359                 res.status = csastat2accessstat(stat);
360                 return (&res);
361         }
362
363         stat = _DtCmsLookupRangeV4(cal, user, access, args->args.Args_4_u.range,
364                 B_TRUE, 0, 0, NULL, 0, NULL, NULL, NULL,
365                 &res.res.Table_Res_List_4_u.b);
366
367         res.status = csastat2accessstat(stat);
368         return (&res);
369 }
370
371 extern Table_Res_4 *
372 _DtCm_rtable_insert_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
373 {
374         static Table_Res_4      res;
375         _DtCmsCalendar          *cal;
376         CSA_return_code         stat;
377         char                    *author;
378         char                    *user;
379         uint                    access;
380         Appt_4                  *ap, *appt, *prev=NULL, *a;
381         cms_entry               *entry;
382
383         if (debug)
384                 fprintf(stderr, "_DtCm_rtable_insert_4_svc called\n");
385
386         /* clean out left over */
387         if (res.res.Table_Res_List_4_u.a != NULL)
388                 _DtCm_free_appt4(res.res.Table_Res_List_4_u.a);
389
390         res.status = access_other_4;
391         res.res.tag = AP_4;
392         res.res.Table_Res_List_4_u.a = NULL;
393
394         /* check arguments */
395         if (args->target == NULL)
396                 return (&res);
397         if ((ap = args->args.Args_4_u.appt) == NULL)
398                 return (&res);
399
400         /* do some sanity checks before inserting : check appt data */
401         for (appt = args->args.Args_4_u.appt; appt != NULL; appt = appt->next)
402         {
403                 /* ntimes should be 0 or positive */
404                 if (appt->ntimes < 0 ||
405                     (appt->period.period > single_4 && appt->ntimes == 0))
406                         return(&res);
407
408                 /* period beyond daysOfWeek is not supported */
409                 if (appt->period.period > daysOfWeek_4) {
410                         res.status = access_notsupported_4;
411                         return(&res);
412                 }
413
414                 /* if weekmask of daysOfWeek appt is set incorrectly, return */
415                 if (appt->period.period == daysOfWeek_4 &&
416                         (appt->period.nth == 0 || appt->period.nth > 127))
417                         return(&res);
418         }
419
420         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
421             &access, &cal)) == CSA_SUCCESS) {
422                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
423                         if (!_DTCMS_HAS_V4_WRITE_ACCESS(access)) {
424                                 res.status = access_failed_4;
425                                 return (&res);
426                         }
427                 } else if (!_DTCMS_HAS_INSERT_ACCESS(access)) {
428                         res.status = access_failed_4;
429                         return (&res);
430                 } else
431                         res.status = access_ok_4;
432         } else {
433                 res.status = csastat2accessstat(stat);
434                 return (&res);
435         }
436
437         /* make copy of the appointments */
438         /* this copy is used in the result and will be freed
439          * when this routine is called again
440          */
441         if ((appt = _DtCm_copy_appt4(args->args.Args_4_u.appt)) == NULL) {
442                 /* memory problem */
443                 return (&res);
444         }
445
446         ap = appt;
447         while (appt != NULL) {
448
449                 /*
450                  * we used to calculate the correct start day,
451                  * but we should return an error instead
452                  */
453                 _DtCms_adjust_appt_startdate(appt);
454
455                 if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
456                         /* convert to attributes */
457                         if (_DtCmsAppt4ToCmsentry(args->target, appt, &entry,
458                             B_TRUE))
459                                 goto insert_error;
460
461                         /* null out readonly attributes */
462                         _DtCm_free_cms_attribute_value(entry->attrs\
463                                 [CSA_ENTRY_ATTR_ORGANIZER_I].value);
464                         entry->attrs[CSA_ENTRY_ATTR_ORGANIZER_I].value = NULL;
465                         _DtCm_free_cms_attribute_value(entry->attrs\
466                                 [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].value);
467                         entry->attrs[CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I].\
468                                 value = NULL;
469
470                         if (_DtCmsCheckInitialAttributes(entry)) {
471                                 _DtCm_free_cms_entry(entry);
472                                 goto insert_error;
473                         }
474
475                         /* set organizer */
476                         if (_DtCm_set_string_attrval(user, &entry->attrs\
477                             [CSA_ENTRY_ATTR_ORGANIZER_I].value,
478                             CSA_VALUE_CALENDAR_USER) != CSA_SUCCESS) {
479                                 _DtCm_free_cms_entry(entry);
480                                 goto insert_error;
481                         }
482
483                         /* insert entry and log it */
484                         if (_DtCmsInsertEntryAndLog(cal, entry)) {
485                                 _DtCm_free_cms_entry(entry);
486                                 goto insert_error;
487                         }
488
489                         appt->appt_id.key = entry->key.id;
490                         _DtCm_free_cms_entry(entry);
491
492                 } else {
493                         if ((a = _DtCm_copy_one_appt4(appt)) == NULL)
494                                 goto insert_error;
495
496                         /* We don't trust the author field; we set our own. */
497                         free(a->author);
498                         if ((a->author = strdup(user)) == NULL) {
499                                 _DtCm_free_appt4(a);
500                                 goto insert_error;
501                         }
502
503                         /* Note, the key in appt will be set if its value is 0. */
504                         if ((stat = _DtCmsInsertApptAndLog(cal, a)) != CSA_SUCCESS) {
505                                 res.status = csastat2accessstat(stat);
506                                 goto insert_error;
507                         }
508
509                         /* get the new key */
510                         appt->appt_id.key = a->appt_id.key;
511                 }
512
513                 prev = appt;
514                 appt = appt->next;
515         }
516
517         cal->modified = B_TRUE;
518
519         /* do callbacks */
520         cal->rlist = _DtCmsDoV1Callback(cal->rlist, user, args->pid, ap);
521
522         for (appt = ap; appt != NULL; appt = appt->next) {
523                 cal->rlist = _DtCmsDoInsertEntryCallback(cal->rlist,
524                                 cal->calendar, user, appt->appt_id.key,
525                                 args->pid);
526         }
527
528         res.status = access_ok_4;
529         res.res.Table_Res_List_4_u.a = ap;
530
531         return (&res);
532
533 insert_error:
534         if (prev != NULL) {
535                 cal->modified = B_TRUE;
536                 /* some appts were inserted successfully */
537                 res.status = access_partial_4;
538                 prev->next = NULL;
539                 res.res.Table_Res_List_4_u.a = ap;
540
541                 /* do callback */
542                 cal->rlist = _DtCmsDoV1Callback(cal->rlist, user, args->pid,
543                         ap);
544                 for (appt = ap; appt != NULL; appt = appt->next) {
545                         cal->rlist = _DtCmsDoInsertEntryCallback(cal->rlist,
546                                         cal->calendar, user,
547                                         appt->appt_id.key, args->pid);
548                 }
549
550         } else {
551                 /* first appt in bunch that failed */
552                 res.status = csastat2accessstat(stat);
553         }
554         _DtCm_free_appt4(appt);
555
556         return (&res);
557 }
558
559 extern Table_Res_4 *
560 _DtCm_rtable_delete_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
561 {
562         static Table_Res_4      res;
563         _DtCmsCalendar          *cal;
564         CSA_return_code         stat;
565         char                    *user;
566         uint                    access;
567         Uidopt_4                *p_keys;
568         Appt_4                  *h = NULL;
569         Appt_4                  *a;
570         int                     d, n, nf;
571         cms_key                 key;
572         cms_entry               *entry;
573
574         if (debug)
575                 fprintf(stderr, "_DtCm_rtable_delete_4_svc called\n");
576
577         /* clean out left over */
578         if (res.res.Table_Res_List_4_u.a)
579                 _DtCm_free_appt4(res.res.Table_Res_List_4_u.a);
580
581         res.status = access_other_4;
582         res.res.tag = AP_4;
583         res.res.Table_Res_List_4_u.a = NULL;
584
585         /* check arguments */
586         if (args->target == NULL)
587                 return (&res);
588         if ((p_keys = args->args.Args_4_u.uidopt) == NULL)
589                 return (&res);
590
591         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
592             &access, &cal)) == CSA_SUCCESS) {
593                 if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
594                     !_DTCMS_HAS_CHANGE_ACCESS(access)) {
595                         res.status = access_failed_4;
596                         return (&res);
597                 }
598         } else {
599                 res.status = csastat2accessstat(stat);
600                 return (&res);
601         }
602
603         nf = d = n = 0;
604         while (p_keys != NULL) {
605                 n++;
606                 if (debug) {
607                         fprintf (stderr, "Delete: (key %ld)%s\n",
608                               p_keys->appt_id.key,
609                               ctime(&p_keys->appt_id.tick));
610                 }
611
612                 if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
613                         key.time = p_keys->appt_id.tick;
614                         key.id = p_keys->appt_id.key;
615
616                         if (p_keys->option == do_all_4)
617                                 stat = _DtCmsDeleteEntryAndLog(cal, user,
618                                         access, &key, &entry);
619                         else
620                                 stat = _DtCmsDeleteInstancesAndLog(cal, user,
621                                         access, &key,
622                                         (p_keys->option == CSA_SCOPE_ONE ?
623                                         CSA_SCOPE_ONE : CSA_SCOPE_FORWARD),
624                                         NULL, &entry); 
625
626                         if (stat == CSA_SUCCESS)
627                                 stat = _DtCm_cms_entry_to_appt4(entry, &a);
628
629                         _DtCm_free_cms_entry(entry);
630
631                 } else {
632
633                         /* single or all in a repeating series */
634                         if (p_keys->option == do_all_4)
635                                 stat = _DtCmsDeleteApptAndLog(cal, user,
636                                         access, &p_keys->appt_id, &a);
637                         else {
638                                 stat = _DtCmsDeleteApptInstancesAndLog(cal,
639                                         user, access, &p_keys->appt_id,
640                                         p_keys->option, NULL, &a);
641                         }
642                 }
643
644                 if (stat == CSA_SUCCESS) {
645                         if (p_keys->option != do_all_4)
646                                 APPT_TICK(a) = p_keys->appt_id.tick;
647
648                         d++;
649                         a->next = h;
650                         h = a;
651                 } else if (stat == CSA_X_DT_E_ENTRY_NOT_FOUND)
652                         nf++;
653
654                 p_keys = p_keys->next;
655         }
656
657         if (h != NULL) {
658                 cal->modified = B_TRUE;
659
660                 /* do callback */
661                 cal->rlist = _DtCmsDoV1Callback(cal->rlist, user, args->pid, h);
662                 for (a = h, p_keys = args->args.Args_4_u.uidopt;
663                     a != NULL; a = a->next) {
664                         while (a->appt_id.key != p_keys->appt_id.key)
665                                 p_keys = p_keys->next;
666
667                         cal->rlist = _DtCmsDoDeleteEntryCallback(cal->rlist,
668                                         cal->calendar, user, a->appt_id.key,
669                                         p_keys->option,
670                                         (p_keys->option == do_all_4 ?
671                                         a->appt_id.tick : p_keys->appt_id.tick),
672                                         args->pid);
673                 }
674         }
675
676         if (d == 0) {
677                 if (stat != 0)
678                         res.status = csastat2accessstat(stat);
679                 else
680                         res.status = (nf < n) ? access_failed_4 : access_ok_4;
681         } else if (d < n)
682                 res.status = access_partial_4;
683         else
684                 res.status = access_ok_4;
685
686         res.res.Table_Res_List_4_u.a = h;
687
688         return (&res);
689 }
690
691 extern Table_Res_4 *
692 _DtCm_rtable_delete_instance_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
693 {
694         static Table_Res_4 res;
695
696         if (debug)
697                 fprintf(stderr, "_DtCm_rtable_delete_instance_4_svc called\n");
698
699         res.status = access_notsupported_4;
700         res.res.tag = AP_4;
701         res.res.Table_Res_List_4_u.a = NULL;
702
703         return(&res);
704 }
705
706 extern Table_Res_4 *
707 _DtCm_rtable_change_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
708 {
709         static Table_Res_4      res;
710         _DtCmsCalendar          *cal;
711         CSA_return_code         stat;
712         char                    *user;
713         uint                    access;
714         Id_4                    *p_key;
715         Appt_4                  *newa, *olda;
716         Appt_4                  tmpappt;
717         Options_4               option;
718         cms_entry               *entry, *oldentry, *newentry = NULL;
719         cms_key                 key;
720
721         if (debug)
722                 fprintf(stderr, "_DtCm_rtable_change_4_svc called\n");
723
724         /* clean out left over */
725         res.status = access_other_4;
726         res.res.tag = AP_4;
727         res.res.Table_Res_List_4_u.a = NULL;
728
729         /* check arguments */
730         if (args->target == NULL)
731                 return (&res);
732         if ((p_key = args->args.Args_4_u.apptid.oid) == NULL)
733                 return (&res);
734         if ((newa = args->args.Args_4_u.apptid.new_appt) == NULL)
735                 return (&res);
736
737         /* ntimes should be 0 or positive */
738         if (newa->ntimes < 0 ||
739             (newa->period.period > single_4 && newa->ntimes == 0))
740                 return(&res);
741
742         /* period beyond daysOfWeek is not supported */
743         if (newa->period.period > daysOfWeek_4) {
744                 res.status = access_notsupported_4;
745                 return(&res);
746         }
747
748         /* if weekmask of daysOfWeek appt is not set correctly, return */
749         if (newa->period.period == daysOfWeek_4 &&
750             (newa->period.nth == 0 || newa->period.nth > 127))
751                 return(&res);
752
753         option = args->args.Args_4_u.apptid.option;
754         if (option < do_all_4 || option > do_forward_4)
755                 return (&res); 
756
757         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
758             &access, &cal)) == CSA_SUCCESS) {
759                 if ((cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
760                     !_DTCMS_HAS_CHANGE_ACCESS(access)) ||
761                     (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
762                     !_DTCMS_HAS_V4_WRITE_ACCESS(access))) {
763
764                         res.status = access_failed_4;
765                         return (&res);
766                 }
767         } else {
768                 res.status = csastat2accessstat(stat);
769                 return (&res);
770         }
771
772         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
773                 /* convert to attributes */
774                 if ((stat = _DtCmsAppt4ToCmsentry(args->target, newa, &entry,
775                     B_TRUE)) == CSA_SUCCESS) {
776
777                         key.time = p_key->tick;
778                         key.id = p_key->key;
779
780                         /* null out readonly attributes */
781                         _DtCm_free_cms_attributes(1,
782                                 &entry->attrs[CSA_ENTRY_ATTR_ORGANIZER_I]);
783                         _DtCm_free_cms_attributes(1, &entry->attrs\
784                                 [CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER_I]);
785                         _DtCm_free_cms_attributes(1,
786                                 &entry->attrs[CSA_ENTRY_ATTR_TYPE_I]);
787
788                         /* update entry */
789                         if (option == do_all_4)
790                                 stat = _DtCmsUpdateEntry(cal, user, access,
791                                         &key, entry->num_attrs,
792                                         &entry->attrs[1], &oldentry, NULL);
793                         else
794                                 stat = _DtCmsUpdateInstances(cal, user,
795                                         access, &key, (option == do_one_4 ?
796                                         CSA_SCOPE_ONE : CSA_SCOPE_FORWARD),
797                                         entry->num_attrs, &entry->attrs[1],
798                                         &oldentry, &newentry);
799
800                         _DtCm_free_cms_entry(entry);
801                 }
802         } else {
803
804                 if (option == do_all_4)
805                         stat = _DtCmsChangeAll(cal, user, access, p_key, newa,
806                                 &olda);
807                 else
808                         stat = _DtCmsChangeSome(cal, user, access,p_key, newa,
809                                 option, &olda);
810         }
811
812         if (stat == CSA_SUCCESS) {
813                 if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
814                         tmpappt.appt_id.tick = oldentry->key.time;
815                         tmpappt.appt_id.key = oldentry->key.id;
816                         olda = &tmpappt;
817                         _DtCm_free_cms_entry(oldentry);
818                         if (newentry) {
819                                 newa->appt_id.key = newentry->key.id;
820                                 _DtCm_free_cms_entry(newentry);
821                         }
822                 }
823
824                 /* If the date/time is changed, we do a callback
825                  * with the old and new appointments.  Otherwise,
826                  * we only do callback with the new appointmnt.
827                  */
828                 if (APPT_TICK(newa) == APPT_TICK(olda)) {
829                         cal->rlist = _DtCmsDoV1Callback(cal->rlist, user,
830                                         args->pid, newa);
831                 } else {
832                         olda->next = newa;
833                         cal->rlist = _DtCmsDoV1Callback(cal->rlist, user,
834                                         args->pid, olda);
835                         olda->next = NULL;
836                 }
837
838                 cal->rlist = _DtCmsDoUpdateEntryCallback(cal->rlist,
839                                 cal->calendar, user,
840                                 (newa->appt_id.key == olda->appt_id.key ?
841                                 0 : newa->appt_id.key),
842                                 olda->appt_id.key, option, (option == do_all_4 ?
843                                 olda->appt_id.tick : p_key->tick),
844                                 args->pid);
845
846                 cal->modified = B_TRUE;
847
848                 /* Return the new appointment. */
849                 res.res.Table_Res_List_4_u.a = newa;
850                 res.status = access_ok_4;
851
852                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION)
853                         _DtCm_free_appt4(olda);
854         } else
855                 res.status = csastat2accessstat(stat);
856
857         return (&res);
858 }
859
860 extern Table_Res_4 *
861 _DtCm_rtable_change_instance_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
862 {
863         static Table_Res_4 res;
864
865         if (debug)
866                 fprintf(stderr, "_DtCm_rtable_change_instance_4_svc called\n");
867
868         res.status = access_notsupported_4;
869         res.res.tag = AP_4;
870         res.res.Table_Res_List_4_u.a = NULL;
871
872         return(&res);
873 }
874
875 extern Table_Res_4 *
876 _DtCm_rtable_lookup_next_reminder_4_svc(
877         Table_Args_4    *args,
878         struct svc_req  *svcrq)
879 {
880         static  Table_Res_4     res;
881         CSA_return_code         stat;
882         char                    *user;
883         uint                    access;
884         _DtCmsCalendar          *cal;
885         Reminder_4              *p_reminder;
886         Rm_que                  *p_node;
887         Rm_que                  *p_prev;
888         Rm_que                  *p_new;
889         Rm_que                  *p_next;
890         time_t                  tick;
891         cms_reminder_ref        *rems;
892
893         if (debug)
894                 fprintf(stderr, "_DtCm_rtable_lookup_next_reminder_4_svc called\n");
895
896         /* clean up left over */
897         if (res.res.Table_Res_List_4_u.r)
898                 _DtCm_free_reminder4(res.res.Table_Res_List_4_u.r);
899
900         res.status = access_other_4;
901         res.res.tag = RM_4;
902         res.res.Table_Res_List_4_u.r = NULL;
903
904         if (args->target == NULL)
905                 return (&res);
906
907         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
908             &access, &cal)) == CSA_SUCCESS) {
909                 /* only user with owner rights can lookup reminders */
910                 if (!(access & CSA_OWNER_RIGHTS)) {
911
912                         res.status = access_failed_4;
913                         return (&res);
914                 }
915         } else {
916                 res.status = csastat2accessstat(stat);
917                 return (&res);
918         }
919
920         tick = args->args.Args_4_u.tick;
921
922         if (debug)
923                 fprintf(stderr, "Next reminder after %s", ctime(&tick));
924
925         if (cal->fversion > 1) {
926                 if ((stat = _DtCmsLookupReminder(cal->remq, tick, 0, NULL,
927                     &rems)) == CSA_SUCCESS) {
928                         stat = _DtCmsReminderRefToReminder(rems,
929                             &res.res.Table_Res_List_4_u.r);
930                         _DtCmsFreeReminderRef(rems);
931                 }
932         } else {
933
934                 stat = _DtCmsGetV4Reminders(cal, tick,
935                         &res.res.Table_Res_List_4_u.r, NULL);
936         }
937
938         res.status =  csastat2accessstat(stat);
939         return (&res);
940 }
941
942 extern Table_Status_4 *
943 _DtCm_rtable_check_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
944 {
945         static  Table_Status_4 s;
946         CSA_return_code stat;
947         Rb_Status rbstat;
948         _DtCmsCalendar  *cal;
949
950         if (debug)
951                 fprintf(stderr, "_DtCm_rtable_check_4_svc called\n");
952
953         if ((stat = _DtCmsGetCalendarByName(args->target, B_TRUE, &cal))
954             != CSA_SUCCESS)
955         {
956                 s = csastat2tablestat(stat);
957                 return (&s);
958         }
959
960         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
961                 s = tbl_notsupported_4;
962         } else {
963
964                 rbstat = rb_check_tree (cal->tree);
965                 if (rbstat == rb_ok)
966                         rbstat = hc_check_list (cal->list);
967
968                 if (rbstat == rb_ok)
969                         s = ok_4;
970                 else
971                         s = other_4;
972         }
973
974         return (&s);
975 }
976
977 extern Table_Status_4 *
978 _DtCm_rtable_flush_table_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
979 {
980         static Table_Status_4 s;
981         Id_4            key;
982         CSA_return_code         stat;
983         _DtCmsCalendar  *cal;
984         int             n = 0;
985         Tree_node       *p_node;
986         List_node       *p_lnode;
987         List_node       *p_next;
988         Appt_4  *p_appt;
989
990         if (!debug) {
991                 s = ok_4;
992                 return (&s);
993         }
994
995         if (debug)
996                 fprintf (stderr, "rtable_flush_table\n");
997
998         if ((stat = _DtCmsGetCalendarByName(args->target, B_TRUE, &cal))
999             != CSA_SUCCESS) {
1000                 s = csastat2tablestat(stat);
1001                 return (&s);
1002         }
1003
1004         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
1005                 s = tbl_notsupported_4;
1006                 return (&s);
1007         }
1008
1009         if (debug)
1010         {
1011                 fprintf(stderr, "%s: before flush.. rbsize= %d\n", pgname,
1012                         rb_size(APPT_TREE(cal))+hc_size(REPT_LIST(cal)));
1013         }
1014         
1015         /* Flushing the single appointment tree. */
1016         key.key = 0;
1017         key.tick = args->args.Args_4_u.tick;
1018         while (p_appt = (Appt_4 *) rb_lookup_next_larger(APPT_TREE(cal),
1019             (caddr_t)&key)) {
1020                 p_node = rb_delete (APPT_TREE(cal),
1021                                 (caddr_t)&(p_appt->appt_id));
1022                 if (p_node != NULL)
1023                 {
1024                         n++;
1025                         _DtCm_free_appt4 ((Appt_4 *) p_node->data);
1026                         free(p_node);
1027                 }
1028         }
1029
1030         /* Flushing the repeating appointment list */
1031         key.key = 0;
1032         key.tick = args->args.Args_4_u.tick;
1033         p_lnode = cal->list->root;
1034         while (p_lnode != NULL)
1035         {
1036                 p_next = hc_lookup_next (p_lnode);
1037                 p_appt = (Appt_4*)p_lnode->data;
1038                 if (APPT_TICK(p_appt) > key.tick)
1039                 {
1040                         n++;
1041                         _DtCm_free_appt4 (p_appt);
1042                         (void) hc_delete_node (REPT_LIST(cal), p_lnode);
1043                         free (p_lnode);
1044                 }
1045                 p_lnode = p_next;
1046         }
1047         s = ok_4;
1048         if (debug)
1049         {
1050                 fprintf (stderr, "%s: entries deleted= %d\n", pgname, n);
1051                 fprintf (stderr, "%s: after flush.. rbsize= %d\n", pgname,
1052                         rb_size(APPT_TREE(cal))+hc_size(REPT_LIST(cal)));
1053         }
1054
1055         return (&s);
1056 }
1057
1058 extern int *
1059 _DtCm_rtable_size_4_svc(Table_Args_4 *args, struct svc_req *svcrq)
1060 {
1061         /* must be static! */
1062         static int size;
1063         CSA_return_code stat;
1064         _DtCmsCalendar  *cal;
1065
1066         if (debug)
1067                 fprintf(stderr, "_DtCm_rtable_size_4_svc called\n");
1068
1069         size = 0;
1070         if ((stat = _DtCmsGetCalendarByName(args->target, B_TRUE, &cal))
1071             != CSA_SUCCESS)
1072                 return(&size);
1073
1074         size = rb_size (APPT_TREE(cal)) + hc_size (REPT_LIST(cal));
1075
1076         return(&size);
1077 }
1078
1079 extern Registration_Status_4 *
1080 _DtCm_register_callback_4_svc(Registration_4 *r, struct svc_req *svcrq)
1081 {
1082         static Registration_Status_4 regstat;
1083         CSA_return_code stat;
1084         char *source;
1085         _DtCmsCalendar *cal = NULL;
1086         _DtCmsRegistrationInfo *copy=NULL;
1087
1088         if (debug)
1089                 fprintf(stderr, "_DtCm_register_callback_4_svc called\n");
1090
1091         /* Error */
1092         if (r->target == NULL) {
1093                 regstat = failed_4;
1094                 return(&regstat);
1095         }
1096
1097         if ((stat = _DtCmsGetClientInfo(svcrq, &source)) != CSA_SUCCESS) {
1098                 regstat = csastat2regstat(stat);
1099                 return (&regstat);
1100         }
1101
1102         /* check if the target exists */
1103         if ((stat = _DtCmsGetCalendarByName(r->target, B_TRUE, &cal))
1104             != CSA_SUCCESS) {
1105                 regstat = csastat2regstat(stat);
1106                 return (&regstat);
1107         }
1108
1109         /* Check for duplicate registrations */
1110         if (_DtCmsGetRegistration(&(cal->rlist), source, r->prognum, r->versnum,
1111             r->procnum, r->pid) == NULL) {
1112                 /* not registered */
1113
1114                 /* Make a copy of the callback info. */
1115
1116                 if ((copy = _DtCmsMakeRegistrationInfo(source, 0, r->prognum,
1117                     r->versnum, r->procnum, r->pid)) == NULL) {
1118                         regstat = failed_4;
1119                         return (&regstat);
1120                 }
1121
1122                 /* Store it away so that it can later be called. */
1123                 copy->next = cal->rlist;
1124                 cal->rlist = copy;
1125
1126                 if (debug) {
1127                         fprintf(stderr, "%s requested registration on %s. registered pid= %d\n",
1128                                 source, r->target, r->pid);
1129                         _DtCmsListRegistration(cal->rlist,
1130                                         cal->calendar);
1131                 }
1132                 regstat = registered_4;
1133                 return(&regstat);
1134         } else {
1135                 /* already registered */
1136                 regstat = registered_4;
1137                 return(&regstat);
1138         }
1139 }
1140
1141 /* de-register an rpc callback proc from the client */
1142 extern Registration_Status_4 *
1143 _DtCm_deregister_callback_4_svc(Registration_4 *r, struct svc_req *svcrq)
1144 {
1145         static Registration_Status_4 regstat;
1146         CSA_return_code stat;
1147         _DtCmsCalendar *cal;
1148         char *source;
1149         _DtCmsRegistrationInfo *p = NULL, *q = NULL;
1150
1151         if (debug)
1152                 fprintf(stderr, "_DtCm_deregister_callback_4_svc called\n");
1153
1154         if (r->target == NULL) {
1155                 regstat = failed_4;
1156                 return(&regstat);
1157         }
1158  
1159         if ((stat = _DtCmsGetClientInfo(svcrq, &source)) != CSA_SUCCESS) {
1160                 regstat = csastat2regstat(stat);
1161                 return (&regstat);
1162         }
1163
1164         if ((stat = _DtCmsGetCalendarByName(r->target, B_FALSE, &cal))
1165             != CSA_SUCCESS) {
1166                 regstat = csastat2regstat(stat);
1167                 return (&regstat);
1168         }
1169
1170         if (cal == NULL) {
1171                 regstat = failed_4;
1172                 return (&regstat);
1173         }
1174
1175         q = p = cal->rlist;
1176         while (p != NULL) {
1177
1178                 /* This says:
1179                  * 1) if the name of the caller requesting deregistration
1180                  * is the same as the original caller who requested
1181                  * requested registration, and
1182                  * 2) if the (transient) program, version, & procnum match
1183                  * the original registration, and
1184                  * 3) if the process id of the client matches the
1185                  *  orignal registration 
1186                  *  
1187                  *  ... only then is it ok to decommission the ticket.
1188                  */
1189
1190
1191                 if ((strcmp(p->client, source)==0) &&
1192                     (p->prognum==r->prognum) &&
1193                     (p->versnum==r->versnum) &&
1194                     (p->procnum==r->procnum) &&
1195                     (p->pid==r->pid)) { /* a match */
1196                         if (debug) {
1197                                 fprintf(stderr, "%s requested deregistration on %s. registered pid= %d\n", source, r->target, r->pid);
1198                         }
1199                         if (p==q)
1200                                 cal->rlist = p->next;
1201                         else
1202                                 q->next = p->next;
1203                         _DtCmsFreeRegistrationInfo(p);
1204                         if (debug) {
1205                                 _DtCmsListRegistration(cal->rlist,
1206                                         cal->calendar);
1207                         }
1208                         regstat = deregistered_4;
1209                         return(&regstat);
1210                 }
1211                 q = p;
1212                 p = p->next;
1213         }
1214
1215         /* not found */
1216         regstat = failed_4;
1217         return(&regstat);
1218 }
1219
1220 extern Access_Status_4 *
1221 _DtCm_rtable_set_access_4_svc(Access_Args_4 *args, struct svc_req *svcrq)
1222 {
1223         /* must be static! */
1224         static Access_Status_4  s;
1225         CSA_return_code         stat;
1226         char                    *user;
1227         uint                    access;
1228         _DtCmsCalendar          *cal = NULL;
1229
1230         if (debug)
1231                 fprintf(stderr, "_DtCm_rtable_set_access_4_svc called\n");
1232
1233         s = access_other_4;
1234
1235         if (args->target == NULL)
1236                 return (&s);
1237
1238         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
1239             &access, &cal)) == CSA_SUCCESS) {
1240                 /* only user with owner rights can lookup reminders */
1241                 if ((cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
1242                     !(access&(CSA_OWNER_RIGHTS|CSA_CHANGE_CALENDAR_ATTRIBUTES)))
1243                     || (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
1244                     !(access & CSA_OWNER_RIGHTS))) {
1245
1246                         s = access_failed_4;
1247                         return (&s);
1248                 }
1249         } else {
1250                 s = csastat2accessstat(stat);
1251                 return(&s);
1252         }
1253
1254         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
1255                 cms_attribute           attr;
1256                 cms_attribute_value     attrval;
1257                 cms_access_entry        *alist;
1258
1259                 if (args->access_list && (alist =
1260                     _DtCmsConvertV4AccessList(args->access_list)) == NULL) {
1261                         s = csastat2accessstat(CSA_E_INSUFFICIENT_MEMORY);
1262                         return (&s);
1263                 }
1264
1265                 attr.name.name = CSA_CAL_ATTR_ACCESS_LIST;
1266                 attr.name.num = 0;
1267                 attr.value = &attrval;
1268                 attrval.type = CSA_VALUE_ACCESS_LIST;
1269                 attrval.item.access_list_value = alist;
1270
1271                 stat = _DtCmsUpdateCalAttributesAndLog(cal, 1, &attr, access);
1272         } else {
1273                 stat = _DtCmsSetV4AccessListAndLog(cal, args->access_list);
1274         }
1275
1276         if ((s = csastat2accessstat(stat)) == access_ok_4) {
1277
1278                 cms_attribute   attr;
1279
1280                 attr.name.name = CSA_CAL_ATTR_ACCESS_LIST;
1281                 cal->rlist = _DtCmsDoUpdateCalAttrsCallback(cal->rlist,
1282                                 cal->calendar, user, 1, &attr, -1);
1283         }
1284
1285         return(&s);
1286 }
1287
1288 extern Access_Args_4 *
1289 _DtCm_rtable_get_access_4_svc(Access_Args_4 *args, struct svc_req *svcrq)
1290 {
1291         static Access_Args_4    res;
1292         CSA_return_code         stat;
1293         _DtCmsCalendar          *cal;
1294         char                    *target, *user;
1295         uint                    access;
1296         boolean_t               useronly = B_FALSE;
1297         cms_access_entry        aentry;
1298
1299         if (debug)
1300                 fprintf(stderr, "_DtCm_rtable_get_access_4_svc called\n");
1301
1302         if (res.target != NULL)
1303                 free(res.target);
1304         if (res.access_list)
1305                 _DtCm_free_access_list4(res.access_list);
1306
1307         res.target = NULL;
1308         res.access_list = (Access_Entry_4 *) NULL;
1309
1310         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
1311             &access, &cal)) == CSA_SUCCESS) {
1312                 if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION &&
1313                     !_DTCMS_HAS_VIEW_CALENDAR_ATTR_ACCESS(access)) {
1314                         useronly = B_TRUE;
1315                 }
1316         } else {
1317                 return (&res);
1318         }
1319
1320         if (cal->fversion > 1) {
1321                 if (useronly) {
1322                         aentry.user = user;
1323                         aentry.rights = access;
1324                         aentry.next = NULL;
1325                         res.access_list = _DtCmsConvertV5AccessList(&aentry,
1326                                                 B_TRUE);
1327                 } else {
1328                         res.access_list = _DtCmsConvertV5AccessList(
1329                                         cal->attrs[CSA_CAL_ATTR_ACCESS_LIST_I].\
1330                                         value->item.access_list_value, B_TRUE);
1331                 }
1332         } else {
1333                 res.access_list = _DtCmsCalendarAccessList(cal);
1334                 if (debug)
1335                         _DtCmsShowAccessList (res.access_list);
1336         }
1337
1338         res.target = strdup(args->target); 
1339
1340         return (&res);
1341 }
1342
1343 extern Table_Res_4 *
1344 _DtCm_rtable_abbreviated_lookup_key_range_4_svc(
1345         Table_Args_4    *args,
1346         struct svc_req  *svcrq)
1347 {
1348         static  Table_Res_4 res;
1349         CSA_return_code stat;
1350         _DtCmsCalendar  *cal;
1351         char            *user;
1352         uint            access;
1353         Keyrange_4      *p_range;
1354         Abb_Appt_4      *abbr_r = NULL;
1355
1356         if (debug)
1357                 fprintf(stderr,
1358                     "_DtCm_rtable_abbreviated_lookup_key_range_4_svc called\n");
1359
1360         if (res.res.Table_Res_List_4_u.b)
1361                 _DtCm_free_abbrev_appt4(res.res.Table_Res_List_4_u.b);
1362
1363         res.res.tag = AB_4;
1364         res.res.Table_Res_List_4_u.b = NULL;
1365
1366         res.status = access_other_4;
1367         if ((p_range = args->args.Args_4_u.keyrange) == NULL)
1368                 return (&res);
1369
1370         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
1371             &access, &cal)) == CSA_SUCCESS) {
1372                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
1373                         if (_DTCMS_HAS_V4_BROWSE_ACCESS(access))
1374                                 res.status = access_ok_4;
1375                         else
1376                                 res.status = access_failed_4;
1377                 }
1378         } else {
1379                 res.status = csastat2accessstat(stat);
1380                 return (&res);
1381         }
1382
1383         while (p_range != NULL)
1384         {
1385
1386                 if ((stat = _DtCmsLookupKeyrangeV4(cal, user, access,
1387                     B_FALSE, B_TRUE, p_range->tick1, p_range->tick2, 0, 0,
1388                     p_range->key, NULL, 0, NULL, NULL, NULL, &abbr_r))
1389                     != CSA_SUCCESS) {
1390                         break;
1391                 }
1392
1393                 p_range = p_range->next;
1394         }
1395
1396         if (stat == CSA_SUCCESS)
1397                 res.res.Table_Res_List_4_u.b = abbr_r;
1398         else
1399                 _DtCm_free_abbrev_appt4(abbr_r);
1400
1401         res.status = csastat2accessstat(stat);
1402         return (&res);
1403 }
1404
1405 extern long *
1406 _DtCm_rtable_gmtoff_4_svc(void *args, struct svc_req *svcrq)
1407 {
1408         static long gmtoff;
1409         extern long timezone;
1410
1411         if (debug)
1412                 fprintf(stderr, "_DtCm_rtable_gmtoff_4_svc called\n");
1413
1414         gmtoff = timezone;
1415         return(&gmtoff);
1416 }
1417
1418 extern Table_Status_4 *
1419 _DtCm_rtable_create_4_svc(Table_Op_Args_4 *args, struct svc_req *svcrq)
1420 {
1421         static  Table_Status_4 res;
1422         CSA_return_code stat;
1423         _DtCmsCalendar  *cal;
1424         char    *source;
1425         char    *calname;
1426         char    *log;
1427         char    *ptr;
1428         char    *id, *user, *domain;
1429         Access_Entry_4 aentry;
1430
1431         if (debug)
1432                 fprintf(stderr, "_DtCm_rtable_create_4_svc called\n");
1433
1434         res = other_4;
1435         if ((stat = _DtCmsGetClientInfo(svcrq, &source)) != CSA_SUCCESS) {
1436                 res = csastat2tablestat(stat);
1437                 return (&res);
1438         }
1439
1440         /* check domain if domain info is available */
1441         /* only user in the local domain can create file */
1442         if (ptr = strchr(source, '.')) {
1443                 if (debug)
1444                         fprintf(stderr, "rpc.cmsd: %s %s(target) and %s(sender)\n",
1445                                 "check domains, comparing",
1446                                 args->target, source);
1447
1448                 if ((domain = _DtCmsTarget2Domain(args->target)) != NULL) {
1449
1450                         if (!_DtCmIsSamePath(domain, ++ptr)) {
1451                                 res = denied_4;
1452                                 free(domain);
1453                                 return(&res);
1454                         }
1455                 }
1456         }
1457
1458         if ((calname = _DtCmsTarget2Name(args->target)) == NULL)
1459                 return(&res);
1460
1461         /* if the file is loaded in memory, the file already exists */
1462         if ((stat = _DtCmsGetCalendarByName(calname, B_FALSE, &cal))
1463             == CSA_SUCCESS && cal != NULL) {
1464                 res = tbl_exist_4;
1465                 free(calname);
1466                 return(&res);
1467         }
1468
1469         /*
1470          * If identifier of the calendar name is a user name,
1471          * make sure it's the same as sender.
1472          * format of calendar name assumed: identifier.name
1473          */
1474         if ((id = _DtCmGetPrefix(calname, '.')) == NULL) {
1475                 free(calname);
1476                 return(&res);
1477         }
1478         if ((user = _DtCmGetPrefix(source, '@')) == NULL) {
1479                 free(calname);
1480                 free(id);
1481                 return(&res);
1482         }
1483
1484         if (getpwnam(id) && strcmp(user, id)) {
1485                 free(calname);
1486                 free(id);
1487                 free(user);
1488                 res = denied_4;
1489                 return(&res);
1490         }
1491         free(id);
1492
1493         if ((log = _DtCmsGetLogFN(calname)) == NULL)
1494         {
1495                 free(calname);
1496                 free(user);
1497                 return(&res);
1498         } else if (debug)
1499                 fprintf(stderr, "new file = '%s'\n", log);
1500
1501         if ((stat = _DtCmsCreateLogV1(user, log)) == CSA_E_CALENDAR_EXISTS)
1502                 res = tbl_exist_4;
1503         else if (stat != CSA_SUCCESS)
1504                 res = other_4;
1505         else
1506                 res = ok_4;
1507
1508         /* initialize the access list to be "WORLD", access_read_4 */
1509         aentry.next = NULL;
1510         aentry.who = WORLD;
1511         aentry.access_type = access_read_4;
1512         if ((stat = _DtCmsAppendAccessByFN(log, access_read_4, &aentry))
1513             != CSA_SUCCESS) {
1514                 unlink(log);
1515         }
1516
1517         free(user);
1518         free(calname);
1519         free(log);
1520         return (&res);
1521 }
1522
1523 extern Table_Status_4 *
1524 _DtCm_rtable_remove_4_svc(Table_Op_Args_4 *args, struct svc_req *svcrq)
1525 {
1526         static  Table_Status_4 res;
1527
1528         res = tbl_notsupported_4;
1529         return (&res);
1530 }
1531
1532 extern Table_Status_4 *
1533 _DtCm_rtable_rename_4_svc(Table_Op_Args_4 *args, struct svc_req *svcrq)
1534 {
1535         static  Table_Status_4 res;
1536
1537         res = tbl_notsupported_4;
1538         return (&res);
1539 }
1540
1541 extern void *
1542 _DtCm_rtable_ping_4_svc(void *args, struct svc_req *svcrq)
1543 {
1544         char dummy;
1545
1546         if (debug)
1547                 fprintf(stderr, "_DtCm_rtable_ping_4_svc called\n");
1548
1549         return((void *)&dummy); /* for RPC reply */
1550 }
1551
1552 extern void
1553 initrtable4(program_handle ph)
1554 {
1555         ph->program_num = TABLEPROG;
1556         ph->prog[TABLEVERS_4].vers = &tableprog_4_table[0];
1557         ph->prog[TABLEVERS_4].nproc = sizeof(tableprog_4_table)/sizeof(tableprog_4_table[0]);
1558 }
1559
1560 /******************************************************************************
1561  * static functions used within the file
1562  ******************************************************************************/
1563
1564 static Exception_4
1565 append_exception_list(Appt_4 *p_appt, int ordinal)
1566 {
1567         Exception_4 p_excpt;
1568         Exception_4 p_prev;
1569         Exception_4 p_ex;
1570
1571         if ((p_excpt = (Exception_4)calloc(1, sizeof(*p_excpt))) == NULL)
1572                 return (NULL);
1573         p_excpt->ordinal = ordinal;
1574         p_prev = NULL;
1575         p_ex = p_appt->exception;
1576         while (p_ex != NULL)
1577         {
1578                 /* Exception list is in descending order for faster access */
1579                 if (ordinal > p_ex->ordinal)
1580                         break;
1581                 p_prev = p_ex;
1582                 p_ex = p_ex->next;
1583         }
1584         if (p_prev == NULL)
1585         {
1586                 p_excpt->next = p_appt->exception;
1587                 p_appt->exception = p_excpt;
1588         }
1589         else
1590         {
1591                 p_excpt->next = p_prev->next;
1592                 p_prev->next = p_excpt;
1593         }
1594
1595         return (p_excpt);
1596 }
1597
1598 static Appt_4 *
1599 rtable_lookup_internal(_DtCmsCalendar *cal, char **p_src, Id_4 *key)
1600 {
1601         Appt_4  *p_appt;
1602         Privacy_Level_4 p_level;
1603
1604         /* Check if it hits a single appointment */
1605         p_appt = (Appt_4 *)rb_lookup(APPT_TREE(cal), (caddr_t)key);
1606
1607         if (p_appt != NULL) {
1608                 switch (_DtCmCheckPrivacyLevel(p_src, p_appt)) {
1609                 case public_4:
1610                         p_appt = _DtCm_copy_one_appt4(p_appt);
1611                         return(p_appt);
1612                 case semiprivate_4:
1613                         p_appt = _DtCm_copy_semiprivate_appt4(p_appt);
1614                         return(p_appt);
1615                 case private_4:
1616                 default:
1617                         return(NULL);
1618                 }
1619         }
1620         
1621         /* Check if it hits an event in any repeating appointment */
1622         p_appt = (Appt_4 *) hc_lookup (REPT_LIST(cal), (caddr_t)key);
1623
1624         if (p_appt != NULL) {
1625                 if ((p_level = _DtCmCheckPrivacyLevel(p_src, p_appt)) != private_4)
1626                 {
1627                         if (_DtCms_in_repeater (key, p_appt, B_FALSE)) {
1628                                 if (p_level == public_4)
1629                                         p_appt = _DtCm_copy_one_appt4(p_appt);
1630                                 else
1631                                         p_appt = _DtCm_copy_semiprivate_appt4(p_appt);
1632                                 APPT_TICK(p_appt) = key->tick;
1633                                 return(p_appt);
1634                         }
1635                 }
1636         }
1637
1638         return (NULL);
1639 }
1640
1641 static Appt_4 *
1642 repeater_next_larger(List_node *p_lnode, Id_4 *key)
1643 {
1644         static Appt_4 appt;
1645         Period_4        period;
1646         Appt_4  *p_appt;
1647         Appt_4  *p_save = NULL;
1648         Id_4    id, next_larger_id;
1649         int     ord;
1650         int     ntimes;
1651
1652         next_larger_id.tick = MAXINT;
1653         next_larger_id.key = MAXINT;
1654         while (p_lnode != NULL)
1655         {
1656                 p_appt = (Appt_4*)p_lnode->data;
1657
1658                 /* check last tick: if it's before the lookup range, skip it */
1659                 if (p_lnode->lasttick == 0)
1660                         p_lnode->lasttick = _DtCms_last_tick_v4(APPT_TICK(p_appt),
1661                                 p_appt->period, p_appt->ntimes);
1662
1663                 if ((p_lnode->lasttick < key->tick) ||
1664                     (p_lnode->lasttick == key->tick &&
1665                     APPT_KEY(p_appt) <= key->key)) {
1666                         p_lnode = hc_lookup_next(p_lnode);
1667                         continue;
1668                 }
1669
1670                 period = p_appt->period;
1671                 ntimes = _DtCms_get_ninstance_v4(p_appt);
1672                 id.tick = _DtCms_closest_tick_v4(key->tick, APPT_TICK(p_appt),
1673                                 period, &ord);
1674                 ord--;
1675                 id.key = APPT_KEY(p_appt);
1676                 while (++ord <= ntimes)
1677                 {
1678                         if ((id.tick < key->tick) || (id.tick == key->tick &&
1679                                         id.key <= key->key))
1680                                 id.tick = _DtCms_next_tick_v4 (id.tick, period);
1681                         else if (!_DtCms_marked_4_cancellation (p_appt, ord)) {
1682                                 if ((id.tick < next_larger_id.tick) ||
1683                                     (id.tick == next_larger_id.tick &&
1684                                         id.key < next_larger_id.key))
1685                                 {
1686                                         next_larger_id = id;
1687                                         p_save = p_appt;
1688                                 }
1689                                 break;
1690                         } else
1691                                 id.tick = _DtCms_next_tick_v4(id.tick, period);
1692                 }
1693                 p_lnode = hc_lookup_next (p_lnode);
1694         }
1695
1696         if (p_save != NULL)
1697         {
1698                 appt = *p_save;
1699                 APPT_TICK(&appt) = next_larger_id.tick;
1700                 p_save = &appt;
1701         }
1702
1703         return (p_save);
1704 }
1705
1706 static Appt_4 *
1707 repeater_next_smaller(List_node *p_lnode, Id_4 *key)
1708 {
1709         static Appt_4 appt;
1710         Appt_4  *p_appt;
1711         Period_4        period;
1712         Appt_4  *p_save = NULL;
1713         Id_4    id, next_smaller_id;
1714         int     ord;
1715         int     ntimes;
1716
1717         next_smaller_id.tick = 0;
1718         next_smaller_id.key = 0;
1719         while (p_lnode != NULL)
1720         {
1721                 p_appt = (Appt_4*)p_lnode->data;
1722                 ord = 0;
1723                 ntimes = _DtCms_get_ninstance_v4(p_appt);
1724                 period = p_appt->period;
1725                 id.tick = APPT_TICK(p_appt);
1726                 id.key = APPT_KEY(p_appt);
1727
1728                 /* Very inefficient loop because it has to check if each
1729                  * instance is cancelled.  If there is a function to calculate
1730                  * last tick, this loop can be rewritten in an efficient way.
1731                  */
1732                 while ((++ord <= ntimes) && (id.tick <= key->tick))
1733                 {
1734                         if (id.tick == key->tick && id.key >= key->key)
1735                                 /* this will get us out of the loop */
1736                                 /* faster than continue.            */
1737                                 id.tick = _DtCms_next_tick_v4 (id.tick, period);
1738                         else {
1739                                 if (!_DtCms_marked_4_cancellation (p_appt, ord)) {
1740                                         if ((id.tick > next_smaller_id.tick) ||
1741                                             (id.tick == next_smaller_id.tick &&
1742                                                 id.key > next_smaller_id.key))
1743                                         {
1744                                                 next_smaller_id = id;
1745                                                 p_save = p_appt;
1746                                         }
1747                                 }
1748                                 id.tick = _DtCms_next_tick_v4 (id.tick, period);
1749                         }
1750                 }
1751
1752                 p_lnode = hc_lookup_next (p_lnode);
1753         }
1754
1755         if (p_save != NULL)
1756         {
1757                 appt = *p_save;
1758                 APPT_TICK(&appt) = next_smaller_id.tick;
1759                 p_save = &appt;
1760         }
1761
1762         return (p_save);
1763 }
1764
1765 static Table_Res_4 *
1766 table_lookup_next(
1767         Table_Args_4 *args,
1768         struct svc_req *svcrq,
1769         caddr_t (* rb_func)(),
1770         Appt_4 *(* rp_func)())
1771 {
1772         static  Table_Res_4 res;
1773         CSA_return_code stat;
1774         Privacy_Level_4 p_level;
1775         Id_4    key;
1776         _DtCmsCalendar *cal;
1777         Appt_4  *p_appt;
1778         Appt_4  *p_appt1;
1779         char    *user;
1780         uint    access;
1781
1782         if (res.res.Table_Res_List_4_u.a)
1783                 _DtCm_free_appt4(res.res.Table_Res_List_4_u.a);
1784
1785         res.status = access_other_4;
1786         res.res.tag = AP_4;
1787         res.res.Table_Res_List_4_u.a = NULL;
1788
1789         switch (args->args.tag) {
1790         case TICK_4:
1791                 key.tick = args->args.Args_4_u.tick;
1792                 key.key = 0;
1793                 break;
1794         case UID_4:
1795                 key = args->args.Args_4_u.key->appt_id;
1796                 break;
1797         default:
1798                 return(&res);
1799         }
1800
1801         if ((stat = _DtCmsV4LoadAndCheckAccess(svcrq, args->target, &user,
1802             &access, &cal)) == CSA_SUCCESS) {
1803                 if (cal->fversion < _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
1804                         if (_DTCMS_HAS_V4_BROWSE_ACCESS(access))
1805                                 res.status = access_ok_4;
1806                         else
1807                                 res.status = access_failed_4;
1808                 }
1809         } else {
1810                 res.status = csastat2accessstat(stat);
1811                 return (&res);
1812         }
1813
1814         if (cal->fversion >= _DtCM_FIRST_EXTENSIBLE_DATA_VERSION) {
1815                 res.status = access_notsupported_4;
1816                 return (&res);
1817         }
1818
1819         p_appt = (Appt_4 *) (*rb_func) (APPT_TREE(cal), &key);
1820         p_appt1 = (*rp_func) (REPT_LIST(cal)->root, &key);
1821
1822         if (p_appt1 != NULL) {
1823                 if (rb_func == rb_lookup_next_larger) {
1824                         if ((p_appt==NULL) ||
1825                             (APPT_TICK(p_appt) > APPT_TICK(p_appt1)) ||
1826                             ((APPT_TICK(p_appt) == APPT_TICK(p_appt1)) &&
1827                                 (APPT_KEY(p_appt) > APPT_KEY(p_appt1)))) {
1828                                 p_appt = p_appt1;
1829                         }
1830                 } else {
1831                         if ((p_appt==NULL) ||
1832                             (APPT_TICK(p_appt) < APPT_TICK(p_appt1)) ||
1833                             ((APPT_TICK(p_appt) == APPT_TICK(p_appt1)) &&
1834                                 (APPT_KEY(p_appt) < APPT_KEY(p_appt1)))) {
1835                                 p_appt = p_appt1;
1836                         }
1837                 }
1838         }
1839
1840         if (p_appt != NULL) {
1841                 switch (_GetAccessLevel(user, access, p_appt)) {
1842                 case public_4:
1843                         p_appt = _DtCm_copy_one_appt4(p_appt);
1844                         break;
1845                 case semiprivate_4:
1846                         p_appt = _DtCm_copy_semiprivate_appt4(p_appt);
1847                         break;
1848                 default:
1849                         p_appt = NULL;
1850                         break;
1851                 }
1852         }
1853
1854         res.res.Table_Res_List_4_u.a = p_appt;
1855         return (&res);
1856 }
1857
1858 static Access_Status_4
1859 csastat2accessstat(CSA_return_code stat)
1860 {
1861         switch (stat) {
1862         case CSA_SUCCESS:
1863                 return (access_ok_4);
1864         case CSA_E_CALENDAR_EXISTS:
1865                 return (access_exists_4);
1866         case CSA_E_CALENDAR_NOT_EXIST:
1867                 return (access_notable_4);
1868         case CSA_E_INSUFFICIENT_MEMORY:
1869         case CSA_E_NO_AUTHORITY:
1870                 return (access_failed_4);
1871         case CSA_X_DT_E_BACKING_STORE_PROBLEM:
1872         case CSA_E_DISK_FULL:
1873                 return (access_incomplete_4);
1874         case CSA_E_NOT_SUPPORTED:
1875                 return (access_notsupported_4);
1876         case CSA_E_INVALID_PARAMETER:
1877         case CSA_E_FAILURE:
1878         case CSA_X_DT_E_ENTRY_NOT_FOUND:
1879         default:
1880                 return (access_other_4);
1881         }
1882 }
1883
1884 static Table_Status_4
1885 csastat2tablestat(CSA_return_code stat)
1886 {
1887         switch (stat) {
1888         case CSA_SUCCESS:
1889                 return (ok_4);
1890         case CSA_E_CALENDAR_EXISTS:
1891                 return (tbl_exist_4);
1892         case CSA_E_CALENDAR_NOT_EXIST:
1893                 return (notable_4);
1894         case CSA_E_NOT_SUPPORTED:
1895                 return (tbl_notsupported_4);
1896         case CSA_E_FAILURE:
1897         case CSA_E_INSUFFICIENT_MEMORY:
1898         case CSA_E_INVALID_PARAMETER:
1899         default:
1900                 return (other_4);
1901         }
1902 }
1903
1904 static Registration_Status_4
1905 csastat2regstat(CSA_return_code stat)
1906 {
1907         switch (stat) {
1908         case CSA_E_CALENDAR_NOT_EXIST:
1909                 return(reg_notable_4);
1910         default:
1911                 return (failed_4);
1912         }
1913 }
1914