845bab812c520108243c98aa41e73232ffd414b2
[openafs.git] / src / libadmin / pts / afs_ptsAdmin.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <ctype.h>
17
18 #include <rx/rx.h>
19 #include <rx/rxstat.h>
20
21 #include <afs/afs_AdminErrors.h>
22 #include <afs/afs_utilAdmin.h>
23 #include <afs/ptint.h>
24 #include <afs/ptserver.h>
25
26 #include "afs_ptsAdmin.h"
27 #include "../adminutil/afs_AdminInternal.h"
28
29 /*
30  * IsValidCellHandle - validate the cell handle for making pts
31  * requests.
32  *
33  * PARAMETERS
34  *
35  * IN cellHandle - a previously opened cellHandle that is to be validated.
36  *
37  * LOCKS
38  *
39  * No locks are obtained or released by this function
40  *
41  * RETURN CODES
42  *
43  * Returns != 0 upon successful completion.
44  *
45  */
46
47 static int
48 IsValidCellHandle(const afs_cell_handle_p c_handle, afs_status_p st)
49 {
50     int rc = 0;
51     afs_status_t tst = 0;
52
53     if (!CellHandleIsValid((void *)c_handle, &tst)) {
54         goto fail_IsValidCellHandle;
55     }
56
57     if (c_handle->pts_valid == 0) {
58         tst = ADMCLIENTCELLPTSINVALID;
59         goto fail_IsValidCellHandle;
60     }
61
62     if (c_handle->pts == NULL) {
63         tst = ADMCLIENTCELLPTSNULL;
64         goto fail_IsValidCellHandle;
65     }
66     rc = 1;
67
68
69   fail_IsValidCellHandle:
70
71     if (st != NULL) {
72         *st = tst;
73     }
74     return rc;
75 }
76
77
78 /*
79  * TranslatePTSNames - translate character representations of pts names
80  * into their numeric equivalent.
81  *
82  * PARAMETERS
83  *
84  * IN cellHandle - a previously opened cellHandle that corresponds
85  * to the cell where the id's exist.
86  *
87  * IN names - the list of names to be translated.
88  *
89  * OUT ids - the list of translated names
90  *
91  * LOCKS
92  *
93  * No locks are obtained or released by this function
94  *
95  * RETURN CODES
96  *
97  * Returns != 0 upon successful completion.
98  *
99  */
100
101 static int
102 TranslatePTSNames(const afs_cell_handle_p cellHandle, namelist * names,
103                   idlist * ids, afs_status_p st)
104 {
105     int rc = 0;
106     afs_status_t tst = 0;
107     int i;
108     char *p;
109
110     /*
111      * Lowercase the names to translate
112      */
113
114     for (i = 0; i < names->namelist_len; i++) {
115         p = names->namelist_val[i];
116         while (*p) {
117             *p = tolower(*p);
118             p++;
119         }
120     }
121
122     tst = ubik_PR_NameToID(cellHandle->pts, 0, names, ids);
123
124     if (tst) {
125         goto fail_TranslatePTSNames;
126     }
127
128
129     /*
130      * Check to see if the lookup failed
131      */
132
133     for (i = 0; i < ids->idlist_len; i++) {
134         if (ids->idlist_val[i] == ANONYMOUSID) {
135             tst = ADMPTSFAILEDNAMETRANSLATE;
136             goto fail_TranslatePTSNames;
137         }
138     }
139     rc = 1;
140
141   fail_TranslatePTSNames:
142
143     if (st != NULL) {
144         *st = tst;
145     }
146     return rc;
147 }
148
149 /*
150  * TranslateTwoNames - translate two pts names to their pts ids.
151  *
152  * PARAMETERS
153  *
154  * IN cellHandle - a previously opened cellHandle that corresponds
155  * to the cell where the group exists.
156  *
157  * IN id1 - one id to be translated
158  *
159  * IN error1 - the error status to be returned in the event that id1 is
160  * too long.
161  *
162  * IN id2 - one id to be translated
163  *
164  * IN error2 - the error status to be returned in the event that id2 is
165  * too long.
166  *
167  *
168  * OUT idlist - the list of pts id's
169  *
170  * LOCKS
171  *
172  * No locks are obtained or released by this function
173  *
174  * RETURN CODES
175  *
176  * Returns != 0 upon successful completion.
177  *
178  */
179
180 static int
181 TranslateTwoNames(const afs_cell_handle_p c_handle, const char *id1,
182                   afs_status_t error1, const char *id2, afs_status_t error2,
183                   idlist * ids, afs_status_p st)
184 {
185     int rc = 0;
186     afs_status_t tst = 0;
187     namelist names;
188     char tmp_array[2 * PTS_MAX_NAME_LEN];
189
190     /*
191      * Copy the group and user names in order to translate them
192      */
193
194     names.namelist_len = 2;
195     names.namelist_val = (prname *) & tmp_array[0];
196
197     strncpy(names.namelist_val[0], id1, PTS_MAX_NAME_LEN);
198     names.namelist_val[0][PTS_MAX_NAME_LEN - 1] = '\0';
199     strncpy(names.namelist_val[1], id2, PTS_MAX_NAME_LEN);
200     names.namelist_val[1][PTS_MAX_NAME_LEN - 1] = '\0';
201     ids->idlist_val = 0;
202     ids->idlist_len = 0;
203
204     /*
205      * Check that user and group aren't too long
206      * This is a cheaper check than calling strlen
207      */
208
209     if (names.namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
210         tst = error1;
211         goto fail_TranslateTwoNames;
212     }
213
214     if (names.namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
215         tst = error2;
216         goto fail_TranslateTwoNames;
217     }
218
219     /*
220      * Translate user and group into pts ID's
221      */
222
223     if (TranslatePTSNames(c_handle, &names, ids, &tst) == 0) {
224         goto fail_TranslateTwoNames;
225     }
226     rc = 1;
227
228
229   fail_TranslateTwoNames:
230
231     if (st != NULL) {
232         *st = tst;
233     }
234     return rc;
235 }
236
237 /*
238  * TranslateOneName - translate a pts name to its pts id.
239  *
240  * PARAMETERS
241  *
242  * IN cellHandle - a previously opened cellHandle that corresponds
243  * to the cell where the group exists.
244  *
245  * IN userName - the user to be translated.
246  *
247  * OUT idlist - the user pts id.
248  *
249  * LOCKS
250  *
251  * No locks are obtained or released by this function
252  *
253  * RETURN CODES
254  *
255  * Returns != 0 upon successful completion.
256  *
257  */
258
259 static int
260 TranslateOneName(const afs_cell_handle_p c_handle, const char *ptsName,
261                  afs_status_t tooLongError, afs_int32 * ptsId,
262                  afs_status_p st)
263 {
264     int rc = 0;
265     afs_status_t tst = 0;
266     namelist names[1];
267     char tmp_array[PTS_MAX_NAME_LEN];
268     idlist ids;
269
270     /*
271      * Copy the name in order to translate it
272      */
273
274     names[0].namelist_len = 1;
275     names[0].namelist_val = (prname *) & tmp_array[0];
276
277     strncpy((char *)names[0].namelist_val, ptsName, PTS_MAX_NAME_LEN);
278     ((char *)names[0].namelist_val)[PTS_MAX_NAME_LEN - 1] = '\0';
279     ids.idlist_val = 0;
280     ids.idlist_len = 0;
281
282     /*
283      * Check that user isn't too long
284      * This is a cheaper check than calling strlen
285      */
286
287     if (names[0].namelist_val[0][PTS_MAX_NAME_LEN - 1] != 0) {
288         tst = tooLongError;
289         goto fail_TranslateOneName;
290     }
291
292     /*
293      * Translate user into pts ID
294      */
295
296     if (TranslatePTSNames(c_handle, names, &ids, &tst) == 0) {
297         goto fail_TranslateOneName;
298     } else {
299         if (ids.idlist_val != NULL) {
300             *ptsId = *ids.idlist_val;
301             free(ids.idlist_val);
302         }
303     }
304     rc = 1;
305
306
307   fail_TranslateOneName:
308
309     if (st != NULL) {
310         *st = tst;
311     }
312     return rc;
313 }
314
315 /*
316  * TranslatePTSIds - translate numeric representations of pts names
317  * into their character equivalent.
318  *
319  * PARAMETERS
320  *
321  * IN cellHandle - a previously opened cellHandle that corresponds
322  * to the cell where the id's exist.
323  *
324  * IN ids - the list of ids to be translated.
325  *
326  * OUT names - the list of translated names
327  *
328  * LOCKS
329  *
330  * No locks are obtained or released by this function
331  *
332  * RETURN CODES
333  *
334  * Returns != 0 upon successful completion.
335  *
336  */
337
338 static int
339 TranslatePTSIds(const afs_cell_handle_p cellHandle, namelist * names,
340                 idlist * ids, afs_status_p st)
341 {
342     int rc = 0;
343     afs_status_t tst = 0;
344
345     tst = ubik_PR_IDToName(cellHandle->pts, 0, ids, names);
346
347     if (tst) {
348         goto fail_TranslatePTSIds;
349     }
350     rc = 1;
351
352   fail_TranslatePTSIds:
353
354     if (st != NULL) {
355         *st = tst;
356     }
357     return rc;
358 }
359
360 /*
361  * pts_GroupMemberAdd - add one member to a pts group
362  *
363  * PARAMETERS
364  *
365  * IN cellHandle - a previously opened cellHandle that corresponds
366  * to the cell where the group exists.
367  *
368  * IN userName - the name to be added to the group.
369  *
370  * IN groupName - the group to be modified.
371  *
372  * LOCKS
373  *
374  * No locks are obtained or released by this function
375  *
376  * RETURN CODES
377  *
378  * Returns != 0 upon successful completion.
379  *
380  */
381
382 int ADMINAPI
383 pts_GroupMemberAdd(const void *cellHandle, const char *userName,
384                    const char *groupName, afs_status_p st)
385 {
386     int rc = 0;
387     afs_status_t tst = 0;
388     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
389         idlist ids = {0,0};
390
391     /*
392      * Validate arguments
393      */
394
395     if (!IsValidCellHandle(c_handle, &tst)) {
396         goto fail_pts_GroupMemberAdd;
397     }
398
399     if ((userName == NULL) || (*userName == 0)) {
400         tst = ADMPTSUSERNAMENULL;
401         goto fail_pts_GroupMemberAdd;
402     }
403
404     if ((groupName == NULL) || (*groupName == 0)) {
405         tst = ADMPTSGROUPNAMENULL;
406         goto fail_pts_GroupMemberAdd;
407     }
408
409     if (!TranslateTwoNames
410         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
411          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
412         goto fail_pts_GroupMemberAdd;
413     }
414
415     /*
416      * Make the rpc
417      */
418
419     tst =
420         ubik_PR_AddToGroup(c_handle->pts, 0, ids.idlist_val[0],
421                   ids.idlist_val[1]);
422
423     if (tst != 0) {
424         goto fail_pts_GroupMemberAdd;
425     }
426     rc = 1;
427
428   fail_pts_GroupMemberAdd:
429
430     if (ids.idlist_val != 0) {
431         free(ids.idlist_val);
432     }
433
434     if (st != NULL) {
435         *st = tst;
436     }
437     return rc;
438 }
439
440 /*
441  * pts_GroupOwnerChange - change the owner of a group
442  *
443  * PARAMETERS
444  *
445  * IN cellHandle - a previously opened cellHandle that corresponds
446  * to the cell where the group exists.
447  *
448  * IN targetGroup - the group to be modified.
449  *
450  * IN userName - the new owner of the group.
451  *
452  * LOCKS
453  *
454  * No locks are obtained or released by this function
455  *
456  * RETURN CODES
457  *
458  * Returns != 0 upon successful completion.
459  *
460  */
461
462 int ADMINAPI
463 pts_GroupOwnerChange(const void *cellHandle, const char *targetGroup,
464                      const char *newOwner, afs_status_p st)
465 {
466     int rc = 0;
467     afs_status_t tst = 0;
468     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
469     idlist ids;
470
471     memset(&ids, 0, sizeof(ids));
472
473     /*
474      * Validate arguments
475      */
476
477     if (!IsValidCellHandle(c_handle, &tst)) {
478         goto fail_pts_GroupOwnerChange;
479     }
480
481     if ((newOwner == NULL) || (*newOwner == 0)) {
482         tst = ADMPTSNEWOWNERNULL;
483         goto fail_pts_GroupOwnerChange;
484     }
485
486     if ((targetGroup == NULL) || (*targetGroup == 0)) {
487         tst = ADMPTSTARGETGROUPNULL;
488         goto fail_pts_GroupOwnerChange;
489     }
490
491     if (!TranslateTwoNames
492         (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, targetGroup,
493          ADMPTSTARGETGROUPTOOLONG, &ids, &tst)) {
494         goto fail_pts_GroupOwnerChange;
495     }
496
497     /*
498      * Make the rpc
499      */
500
501     tst =
502         ubik_PR_ChangeEntry(c_handle->pts, 0, ids.idlist_val[1], "",
503                   ids.idlist_val[0], 0);
504
505     if (tst != 0) {
506         goto fail_pts_GroupOwnerChange;
507     }
508     rc = 1;
509
510   fail_pts_GroupOwnerChange:
511
512     if (ids.idlist_val != 0) {
513         free(ids.idlist_val);
514     }
515
516     if (st != NULL) {
517         *st = tst;
518     }
519     return rc;
520 }
521
522 /*
523  * pts_GroupCreate - create a new group
524  *
525  * PARAMETERS
526  *
527  * IN cellHandle - a previously opened cellHandle that corresponds
528  * to the cell where the group exists.
529  *
530  * IN newGroup - the group to be created.
531  *
532  * IN newOwner - the owner of the group.  Pass NULL if the current user
533  * is to be the new owner, or the character string of the owner otherwise.
534  *
535  * IN/OUT newGroupId - the pts id of the group.  Pass 0 to have ptserver 
536  * generate a value, != 0 to assign a value on your own.  The group id
537  * that is used to create the group is copied into this parameter in the
538  * event you pass in 0.
539  *
540  * LOCKS
541  *
542  * No locks are obtained or released by this function
543  *
544  * RETURN CODES
545  *
546  * Returns != 0 upon successful completion.
547  *
548  */
549
550 int ADMINAPI
551 pts_GroupCreate(const void *cellHandle, char *newGroup,
552                 char *newOwner, int *newGroupId, afs_status_p st)
553 {
554     int rc = 0;
555     afs_status_t tst = 0;
556     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
557     afs_int32 newOwnerId = 0;
558
559     /*
560      * Validate arguments
561      */
562
563     if (!IsValidCellHandle(c_handle, &tst)) {
564         goto fail_pts_GroupCreate;
565     }
566
567     if ((newGroup == NULL) || (*newGroup == 0)) {
568         tst = ADMPTSNEWGROUPNULL;
569         goto fail_pts_GroupCreate;
570     }
571
572     if (newGroupId == NULL) {
573         tst = ADMPTSNEWGROUPIDNULL;
574         goto fail_pts_GroupCreate;
575     }
576
577     if (*newGroupId > 0) {
578         tst = ADMPTSNEWGROUPIDPOSITIVE;
579         goto fail_pts_GroupCreate;
580     }
581
582     /*
583      * If a newOwner was specified, validate that it exists
584      */
585
586     if (newOwner != NULL) {
587         if (!TranslateOneName
588             (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, &newOwnerId, &tst)) {
589             goto fail_pts_GroupCreate;
590         }
591     }
592
593     /*
594      * We make a different rpc based upon the input to this function
595      */
596
597     if (*newGroupId != 0) {
598         tst =
599             ubik_PR_INewEntry(c_handle->pts, 0, newGroup, *newGroupId,
600                       newOwnerId);
601     } else {
602         tst =
603             ubik_PR_NewEntry(c_handle->pts, 0, newGroup, PRGRP,
604                       newOwnerId, newGroupId);
605     }
606
607     if (tst != 0) {
608         goto fail_pts_GroupCreate;
609     }
610     rc = 1;
611
612   fail_pts_GroupCreate:
613
614     if (st != NULL) {
615         *st = tst;
616     }
617     return rc;
618 }
619
620 /*
621  * GetGroupAccess - a small convenience function for setting
622  * permissions.
623  *
624  * PARAMETERS
625  *
626  * IN access - a pointer to a pts_groupAccess_t to be set with the
627  * correct permission.
628  *
629  * IN flag - the current permission flag used to derive the permission.
630  *
631  * LOCKS
632  *
633  * No locks are obtained or released by this function
634  *
635  * RETURN CODES
636  *
637  * Since this function cannot fail, it returns void.
638  *
639  */
640
641 static void
642 GetGroupAccess(pts_groupAccess_p access, afs_int32 flag)
643 {
644
645     *access = PTS_GROUP_OWNER_ACCESS;
646     if (flag == 0) {
647         *access = PTS_GROUP_OWNER_ACCESS;
648     } else if (flag == 1) {
649         *access = PTS_GROUP_ACCESS;
650     } else if (flag == 2) {
651         *access = PTS_GROUP_ANYUSER_ACCESS;
652     }
653 }
654
655
656 /*
657  * pts_GroupGet - retrieve information about a particular group.
658  *
659  * PARAMETERS
660  *
661  * IN cellHandle - a previously opened cellHandle that corresponds
662  * to the cell where the group exists.
663  *
664  * IN groupName - the group to retrieve.
665  *
666  * OUT groupP - a pointer to a pts_GroupEntry_t structure that upon
667  * successful completion is filled with information about groupName.
668  *
669  * LOCKS
670  *
671  * No locks are obtained or released by this function
672  *
673  * RETURN CODES
674  *
675  * Returns != 0 upon successful completion.
676  *
677  */
678
679 int ADMINAPI
680 pts_GroupGet(const void *cellHandle, const char *groupName,
681              pts_GroupEntry_p groupP, afs_status_p st)
682 {
683     int rc = 0;
684     afs_status_t tst = 0;
685     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
686     afs_int32 groupId = 0;
687     afs_int32 flags;
688     afs_int32 twobit;
689     struct prcheckentry groupEntry;
690     idlist ids;
691     afs_int32 ptsids[2];
692     namelist names;
693
694     /*
695      * Validate arguments
696      */
697
698     if (!IsValidCellHandle(c_handle, &tst)) {
699         goto fail_pts_GroupGet;
700     }
701
702     if ((groupName == NULL) || (*groupName == 0)) {
703         tst = ADMPTSGROUPNAMENULL;
704         goto fail_pts_GroupGet;
705     }
706
707     if (groupP == NULL) {
708         tst = ADMPTSGROUPPNULL;
709         goto fail_pts_GroupGet;
710     }
711
712     /*
713      * Translate the group name into an id.
714      */
715
716     if (!TranslateOneName
717         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
718         goto fail_pts_GroupGet;
719     }
720
721     /*
722      * Retrieve information about the group
723      */
724
725     tst = ubik_PR_ListEntry(c_handle->pts, 0, groupId, &groupEntry);
726
727     if (tst != 0) {
728         goto fail_pts_GroupGet;
729     }
730
731     groupP->membershipCount = groupEntry.count;
732     groupP->nameUid = groupEntry.id;
733     groupP->ownerUid = groupEntry.owner;
734     groupP->creatorUid = groupEntry.creator;
735     strncpy(groupP->name, groupEntry.name, PTS_MAX_NAME_LEN);
736     groupP->name[PTS_MAX_NAME_LEN - 1] = '\0';
737     /*
738      * Set the access rights based upon the value of the flags member
739      * of the groupEntry struct.
740      *
741      * To the best of my ability to decypher the pts code, it looks like
742      * the rights are stored in flags as follows:
743      *
744      * I number my bits from least significant to most significant starting
745      * with 0.
746      *
747      * remove - bit 0
748      *     if bit 0 == 0 -> r access is denied
749      *     if bit 0 == 1 -> r access is granted
750      *
751      * add - bits 1 and 2
752      *     if bit 2 == 0 and bit 1 == 0 -> a access is denied
753      *     if bit 2 == 0 and bit 1 == 1 -> a access is granted
754      *     if bit 2 == 1 and bit 1 == 0 -> A access is granted
755      *     if bit 2 == 1 and bit 1 == 1 -> this is an error
756      *
757      * membership - bits 3 and 4
758      *     if bit 4 == 0 and bit 3 == 0 -> m access is denied
759      *     if bit 4 == 0 and bit 3 == 1 -> m access is granted
760      *     if bit 4 == 1 and bit 3 == 0 -> M access is granted
761      *     if bit 4 == 1 and bit 3 == 1 -> this is an error
762      *
763      * owned - bit 5
764      *     if bit 5 == 0 -> O access is denied
765      *     if bit 5 == 1 -> O access is granted
766      *
767      * status - bits 6 and 7
768      *     if bit 7 == 0 and bit 6 == 0 -> s access is denied
769      *     if bit 7 == 0 and bit 6 == 1 -> s access is granted
770      *     if bit 7 == 1 and bit 6 == 0 -> S access is granted
771      *     if bit 7 == 1 and bit 6 == 1 -> this is an error
772      *
773      * For cases where the permission doesn't make sense for the
774      * type of entry, or where an error occurs, we ignore it.
775      * This is the behavior of the pts code.
776      */
777
778     flags = groupEntry.flags;
779     if (flags & 1) {
780         groupP->listDelete = PTS_GROUP_ACCESS;
781     } else {
782         groupP->listDelete = PTS_GROUP_OWNER_ACCESS;
783     }
784
785     flags = flags >> 1;
786     twobit = flags & 3;
787
788     GetGroupAccess(&groupP->listAdd, twobit);
789
790     flags = flags >> 2;
791     twobit = flags & 3;
792
793     GetGroupAccess(&groupP->listMembership, twobit);
794
795     flags = flags >> 2;
796
797     if (flags & 1) {
798         groupP->listGroupsOwned = PTS_GROUP_ANYUSER_ACCESS;
799     } else {
800         groupP->listGroupsOwned = PTS_GROUP_OWNER_ACCESS;
801     }
802
803     flags = flags >> 1;
804     twobit = flags & 3;
805
806     GetGroupAccess(&groupP->listStatus, twobit);
807
808     /* 
809      * Make another rpc and translate the owner and creator ids into
810      * character strings.
811      */
812
813     ids.idlist_len = 2;
814     ids.idlist_val = ptsids;
815     ptsids[0] = groupEntry.owner;
816     ptsids[1] = groupEntry.creator;
817     names.namelist_len = 0;
818     names.namelist_val = 0;
819
820
821     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
822         goto fail_pts_GroupGet;
823     }
824
825     strncpy(groupP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
826     groupP->owner[PTS_MAX_NAME_LEN - 1] = '\0';
827     strncpy(groupP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
828     groupP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
829     free(names.namelist_val);
830     rc = 1;
831
832   fail_pts_GroupGet:
833
834     if (st != NULL) {
835         *st = tst;
836     }
837     return rc;
838 }
839
840 /*
841  * EntryDelete - delete a pts entry (group or user).
842  *
843  * PARAMETERS
844  *
845  * IN cellHandle - a previously opened cellHandle that corresponds
846  * to the cell where the group exists.
847  *
848  * IN entryName - the entry to be deleted.
849  *
850  * IN error1 - the error status to be returned in the event that entryName is
851  * null.
852  *
853  * IN error2 - the error status to be returned in the event that entryName is
854  * too long.
855  *
856  * LOCKS
857  *
858  * No locks are obtained or released by this function
859  *
860  * RETURN CODES
861  *
862  * Returns != 0 upon successful completion.
863  *
864  */
865
866 static int
867 EntryDelete(const void *cellHandle, const char *entryName,
868             afs_status_t error1, afs_status_t error2, afs_status_p st)
869 {
870     int rc = 0;
871     afs_status_t tst = 0;
872     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
873     afs_int32 entryId = 0;
874
875     /*
876      * Validate arguments
877      */
878
879     if (!IsValidCellHandle(c_handle, &tst)) {
880         goto fail_EntryDelete;
881     }
882
883     if ((entryName == NULL) || (*entryName == 0)) {
884         tst = error1;
885         goto fail_EntryDelete;
886     }
887
888     /*
889      * Translate the entry name into an id.
890      */
891
892     if (!TranslateOneName(c_handle, entryName, error2, &entryId, &tst)) {
893         goto fail_EntryDelete;
894     }
895
896     /*
897      * Make the rpc
898      */
899
900     tst = ubik_PR_Delete(c_handle->pts, 0, entryId);
901
902     if (tst != 0) {
903         goto fail_EntryDelete;
904     }
905     rc = 1;
906
907   fail_EntryDelete:
908
909     if (st != NULL) {
910         *st = tst;
911     }
912     return rc;
913 }
914
915
916 /*
917  * pts_GroupDelete - delete a group
918  *
919  * PARAMETERS
920  *
921  * IN cellHandle - a previously opened cellHandle that corresponds
922  * to the cell where the group exists.
923  *
924  * IN groupName - the group to be deleted.
925  *
926  * LOCKS
927  *
928  * No locks are obtained or released by this function
929  *
930  * RETURN CODES
931  *
932  * Returns != 0 upon successful completion.
933  *
934  */
935
936 int ADMINAPI
937 pts_GroupDelete(const void *cellHandle, const char *groupName,
938                 afs_status_p st)
939 {
940
941     return EntryDelete(cellHandle, groupName, ADMPTSGROUPNAMENULL,
942                        ADMPTSGROUPNAMETOOLONG, st);
943 }
944
945 /*
946  * pts_GroupMaxGet - get the maximum in use group id.
947  *
948  * PARAMETERS
949  *
950  * IN cellHandle - a previously opened cellHandle that corresponds
951  * to the cell where the group exists.
952  *
953  * OUT maxGroupId - upon successful completion contains the maximum
954  * group Id in use at the server.
955  *
956  * LOCKS
957  *
958  * No locks are obtained or released by this function
959  *
960  * RETURN CODES
961  *
962  * Returns != 0 upon successful completion.
963  *
964  */
965
966 int ADMINAPI
967 pts_GroupMaxGet(const void *cellHandle, int *maxGroupId, afs_status_p st)
968 {
969     int rc = 0;
970     afs_status_t tst = 0;
971     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
972     afs_int32 maxUserId = 0;
973
974     /*
975      * Validate arguments
976      */
977
978     if (!IsValidCellHandle(c_handle, &tst)) {
979         goto fail_pts_GroupMaxGet;
980     }
981
982     if (maxGroupId == NULL) {
983         tst = ADMPTSMAXGROUPIDNULL;
984         goto fail_pts_GroupMaxGet;
985     }
986
987     tst = ubik_PR_ListMax(c_handle->pts, 0, &maxUserId, maxGroupId);
988
989     if (tst != 0) {
990         goto fail_pts_GroupMaxGet;
991     }
992     rc = 1;
993
994   fail_pts_GroupMaxGet:
995
996     if (st != NULL) {
997         *st = tst;
998     }
999     return rc;
1000 }
1001
1002 /*
1003  * pts_GroupMaxSet - set the maximum in use group id.
1004  *
1005  * PARAMETERS
1006  *
1007  * IN cellHandle - a previously opened cellHandle that corresponds
1008  * to the cell where the group exists.
1009  *
1010  * IN maxGroupId - the new maximum group id.
1011  *
1012  * LOCKS
1013  *
1014  * No locks are obtained or released by this function
1015  *
1016  * RETURN CODES
1017  *
1018  * Returns != 0 upon successful completion.
1019  *
1020  */
1021
1022 int ADMINAPI
1023 pts_GroupMaxSet(const void *cellHandle, int maxGroupId, afs_status_p st)
1024 {
1025     int rc = 0;
1026     afs_status_t tst = 0;
1027     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1028
1029     /*
1030      * Validate arguments
1031      */
1032
1033     if (!IsValidCellHandle(c_handle, &tst)) {
1034         goto fail_pts_GroupMaxSet;
1035     }
1036
1037     tst = ubik_PR_SetMax(c_handle->pts, 0, maxGroupId, PRGRP);
1038
1039     if (tst != 0) {
1040         goto fail_pts_GroupMaxSet;
1041     }
1042     rc = 1;
1043
1044   fail_pts_GroupMaxSet:
1045
1046     if (st != NULL) {
1047         *st = tst;
1048     }
1049     return rc;
1050 }
1051
1052 /*
1053  * NOTE
1054  *
1055  * I'm not using the common iterator pattern here since the retrival
1056  * of the member list is actually accomplished in 1 rpc.  There's no
1057  * sense in trying to fit this pts specific behavior into the more
1058  * generic model, so instead the Begin functions actually do all the
1059  * rpc work and the next/done functions just manipulate the retrieved
1060  * data.
1061  */
1062
1063 typedef struct pts_group_member_list_iterator {
1064     int begin_magic;
1065     int is_valid;
1066     pthread_mutex_t mutex;      /* hold to manipulate this structure */
1067     prlist ids;
1068     namelist names;
1069     int index;
1070     int end_magic;
1071 } pts_group_member_list_iterator_t, *pts_group_member_list_iterator_p;
1072
1073 /*
1074  * pts_GroupMemberListBegin - begin iterating over the list of members
1075  * of a particular group.
1076  *
1077  * PARAMETERS
1078  *
1079  * IN iter - an iterator previously returned by pts_GroupMemberListBegin
1080  *
1081  * LOCKS
1082  *
1083  * No locks are obtained or released by this function
1084  *
1085  * RETURN CODES
1086  *
1087  * Returns != 0 upon successful completion.
1088  *
1089  */
1090
1091 static int
1092 IsValidPtsGroupMemberListIterator(pts_group_member_list_iterator_p iter,
1093                                   afs_status_p st)
1094 {
1095     int rc = 0;
1096     afs_status_t tst = 0;
1097
1098     if (iter == NULL) {
1099         tst = ADMITERATORNULL;
1100         goto fail_IsValidPtsGroupMemberListIterator;
1101     }
1102
1103     if ((iter->begin_magic != BEGIN_MAGIC) || (iter->end_magic != END_MAGIC)) {
1104         tst = ADMITERATORBADMAGICNULL;
1105         goto fail_IsValidPtsGroupMemberListIterator;
1106     }
1107
1108     if (iter->is_valid == 0) {
1109         tst = ADMITERATORINVALID;
1110         goto fail_IsValidPtsGroupMemberListIterator;
1111     }
1112     rc = 1;
1113
1114   fail_IsValidPtsGroupMemberListIterator:
1115
1116     if (st != NULL) {
1117         *st = tst;
1118     }
1119     return rc;
1120 }
1121
1122 /*
1123  * MemberListBegin - an internal function which is used to get both
1124  * the list of members in a group and the list of groups a user belongs
1125  * to.
1126  *
1127  * PARAMETERS
1128  *
1129  * IN cellHandle - a previously opened cellHandle that corresponds
1130  * to the cell where the group exists.
1131  *
1132  * IN name - the name whose membership will be retrieved.
1133  *
1134  * OUT iterationIdP - upon successful completion contains a iterator that
1135  * can be passed to pts_GroupMemberListNext or pts_UserMemberListNext
1136  *
1137  * LOCKS
1138  *
1139  * No locks are obtained or released by this function
1140  *
1141  * RETURN CODES
1142  *
1143  * Returns != 0 upon successful completion.
1144  *
1145  */
1146
1147 static int
1148 MemberListBegin(const void *cellHandle, const char *name, afs_status_t error1,
1149                 afs_status_t error2, void **iterationIdP, afs_status_p st)
1150 {
1151     int rc = 0;
1152     afs_status_t tst = 0;
1153     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1154     afs_int32 groupId = 0;
1155     afs_int32 exceeded = 0;
1156     pts_group_member_list_iterator_p iter =
1157         malloc(sizeof(pts_group_member_list_iterator_t));
1158     int iter_allocated = 0;
1159     int ids_allocated = 0;
1160     int names_allocated = 0;
1161     int mutex_inited = 0;
1162
1163     /*
1164      * Validate arguments
1165      */
1166
1167     if (!IsValidCellHandle(c_handle, &tst)) {
1168         goto fail_MemberListBegin;
1169     }
1170
1171     if ((name == NULL) || (*name == 0)) {
1172         tst = ADMPTSGROUPNAMENULL;
1173         goto fail_MemberListBegin;
1174     }
1175
1176     if (iterationIdP == NULL) {
1177         tst = ADMITERATORNULL;
1178         goto fail_MemberListBegin;
1179     }
1180
1181     if (iter == NULL) {
1182         tst = ADMNOMEM;
1183         goto fail_MemberListBegin;
1184     }
1185
1186     iter_allocated = 1;
1187
1188     /*
1189      * Translate the name into an id.
1190      */
1191
1192     if (!TranslateOneName
1193         (c_handle, name, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1194         goto fail_MemberListBegin;
1195     }
1196
1197     if (pthread_mutex_init(&iter->mutex, 0)) {
1198         tst = ADMMUTEXINIT;
1199         goto fail_MemberListBegin;
1200     }
1201
1202     mutex_inited = 1;
1203
1204     iter->ids.prlist_len = 0;
1205     iter->ids.prlist_val = 0;
1206
1207     tst =
1208         ubik_PR_ListElements(c_handle->pts, 0, groupId, &iter->ids,
1209                   &exceeded);
1210
1211     if (tst != 0) {
1212         goto fail_MemberListBegin;
1213     }
1214
1215     if (exceeded != 0) {
1216         tst = ADMPTSGROUPMEMEXCEEDED;
1217         goto fail_MemberListBegin;
1218     }
1219
1220     ids_allocated = 1;
1221     iter->names.namelist_len = 0;
1222     iter->names.namelist_val = 0;
1223
1224     if (!TranslatePTSIds
1225         (c_handle, &iter->names, (idlist *) & iter->ids, &tst)) {
1226         goto fail_MemberListBegin;
1227     }
1228
1229     names_allocated = 1;
1230     iter->begin_magic = BEGIN_MAGIC;
1231     iter->end_magic = END_MAGIC;
1232     iter->index = 0;
1233     iter->is_valid = 1;
1234
1235     *iterationIdP = (void *)iter;
1236     rc = 1;
1237
1238   fail_MemberListBegin:
1239
1240     if (ids_allocated) {
1241         free(iter->ids.prlist_val);
1242     }
1243
1244     if (rc == 0) {
1245         if (names_allocated) {
1246             free(iter->names.namelist_val);
1247         }
1248         if (mutex_inited) {
1249             pthread_mutex_destroy(&iter->mutex);
1250         }
1251         if (iter_allocated) {
1252             free(iter);
1253         }
1254     }
1255     if (st != NULL) {
1256         *st = tst;
1257     }
1258     return rc;
1259 }
1260
1261
1262 /*
1263  * pts_GroupMemberListBegin - begin iterating over the list of members
1264  * of a particular group.
1265  *
1266  * PARAMETERS
1267  *
1268  * IN cellHandle - a previously opened cellHandle that corresponds
1269  * to the cell where the group exists.
1270  *
1271  * IN groupName - the group whose members will be returned.
1272  *
1273  * OUT iterationIdP - upon successful completion contains a iterator that
1274  * can be passed to pts_GroupMemberListNext.
1275  *
1276  * LOCKS
1277  *
1278  * No locks are obtained or released by this function
1279  *
1280  * RETURN CODES
1281  *
1282  * Returns != 0 upon successful completion.
1283  *
1284  */
1285
1286 int ADMINAPI
1287 pts_GroupMemberListBegin(const void *cellHandle, const char *groupName,
1288                          void **iterationIdP, afs_status_p st)
1289 {
1290     return MemberListBegin(cellHandle, groupName, ADMPTSGROUPNAMENULL,
1291                            ADMPTSGROUPNAMETOOLONG, iterationIdP, st);
1292 }
1293
1294 /*
1295  * pts_GroupMemberListNext - get the next member of a group
1296  *
1297  * PARAMETERS
1298  *
1299  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1300  *
1301  * OUT memberName - upon successful completion contains the next member of
1302  * a group.
1303  *
1304  * LOCKS
1305  *
1306  * The iterator mutex is held during the retrieval of the next member.
1307  *
1308  * RETURN CODES
1309  *
1310  * Returns != 0 upon successful completion.
1311  *
1312  */
1313
1314 int ADMINAPI
1315 pts_GroupMemberListNext(const void *iterationId, char *memberName,
1316                         afs_status_p st)
1317 {
1318     int rc = 0;
1319     afs_status_t tst = 0;
1320     pts_group_member_list_iterator_p iter =
1321         (pts_group_member_list_iterator_p) iterationId;
1322     int mutex_locked = 0;
1323
1324     /*
1325      * Validate arguments
1326      */
1327
1328     if (iter == NULL) {
1329         tst = ADMITERATORNULL;
1330         goto fail_pts_GroupMemberListNext;
1331     }
1332
1333     if (memberName == NULL) {
1334         tst = ADMPTSMEMBERNAMENULL;
1335         goto fail_pts_GroupMemberListNext;
1336     }
1337
1338     /*
1339      * Lock the mutex and check the validity of the iterator
1340      */
1341
1342     if (pthread_mutex_lock(&iter->mutex)) {
1343         tst = ADMMUTEXLOCK;
1344         goto fail_pts_GroupMemberListNext;
1345     }
1346
1347     mutex_locked = 1;
1348
1349     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1350         goto fail_pts_GroupMemberListNext;
1351     }
1352
1353     /*
1354      * Check to see if we've copied out all the data.  If we haven't,
1355      * copy another item.  If we have, mark the iterator done.
1356      */
1357
1358     if (iter->index >= iter->names.namelist_len) {
1359         tst = ADMITERATORDONE;
1360         goto fail_pts_GroupMemberListNext;
1361     } else {
1362         strcpy(memberName, iter->names.namelist_val[iter->index]);
1363         iter->index++;
1364     }
1365     rc = 1;
1366
1367   fail_pts_GroupMemberListNext:
1368
1369     if (mutex_locked) {
1370         pthread_mutex_unlock(&iter->mutex);
1371     }
1372
1373     if (st != NULL) {
1374         *st = tst;
1375     }
1376     return rc;
1377 }
1378
1379 /*
1380  * pts_GroupMemberListDone - finish using a member list iterator
1381  *
1382  * PARAMETERS
1383  *
1384  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1385  *
1386  * LOCKS
1387  *
1388  * The iterator is locked and then destroyed
1389  *
1390  * RETURN CODES
1391  *
1392  * Returns != 0 upon successful completion.
1393  *
1394  * ASSUMPTIONS
1395  *
1396  * It is the user's responsibility to make sure pts_GroupMemberListDone
1397  * is called only once for each iterator.
1398  */
1399
1400 int ADMINAPI
1401 pts_GroupMemberListDone(const void *iterationId, afs_status_p st)
1402 {
1403     int rc = 0;
1404     afs_status_t tst = 0;
1405     pts_group_member_list_iterator_p iter =
1406         (pts_group_member_list_iterator_p) iterationId;
1407     int mutex_locked = 0;
1408
1409     /*
1410      * Validate arguments
1411      */
1412
1413     if (iter == NULL) {
1414         tst = ADMITERATORNULL;
1415         goto fail_pts_GroupMemberListDone;
1416     }
1417
1418     /*
1419      * Lock the mutex and check the validity of the iterator
1420      */
1421
1422     if (pthread_mutex_lock(&iter->mutex)) {
1423         tst = ADMMUTEXLOCK;
1424         goto fail_pts_GroupMemberListDone;
1425     }
1426
1427     mutex_locked = 1;
1428
1429     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1430         goto fail_pts_GroupMemberListDone;
1431     }
1432
1433     /*
1434      * Free the namelist and the iterator.
1435      */
1436
1437     pthread_mutex_destroy(&iter->mutex);
1438     mutex_locked = 0;
1439     iter->is_valid = 0;
1440     free(iter->names.namelist_val);
1441     free(iter);
1442     rc = 1;
1443
1444   fail_pts_GroupMemberListDone:
1445
1446     if (mutex_locked) {
1447         pthread_mutex_unlock(&iter->mutex);
1448     }
1449
1450     if (st != NULL) {
1451         *st = tst;
1452     }
1453     return rc;
1454 }
1455
1456 /*
1457  * pts_GroupMemberRemove - remove a member from a group.
1458  *
1459  * PARAMETERS
1460  *
1461  * IN cellHandle - a previously opened cellHandle that corresponds
1462  * to the cell where the group exists.
1463  *
1464  * IN userName - the user to remove.
1465  *
1466  * IN groupName - the group to modify
1467  *
1468  * LOCKS
1469  *
1470  * No locks are held by this function
1471  *
1472  * RETURN CODES
1473  *
1474  * Returns != 0 upon successful completion.
1475  *
1476  */
1477
1478 int ADMINAPI
1479 pts_GroupMemberRemove(const void *cellHandle, const char *userName,
1480                       const char *groupName, afs_status_p st)
1481 {
1482     int rc = 0;
1483     afs_status_t tst = 0;
1484     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1485     idlist ids;
1486
1487     ids.idlist_val = NULL;
1488
1489     /*
1490      * Validate arguments
1491      */
1492
1493     if (!IsValidCellHandle(c_handle, &tst)) {
1494         goto fail_pts_GroupMemberRemove;
1495     }
1496
1497     if ((userName == NULL) || (*userName == 0)) {
1498         tst = ADMPTSUSERNAMENULL;
1499         goto fail_pts_GroupMemberRemove;
1500     }
1501
1502     if ((groupName == NULL) || (*groupName == 0)) {
1503         tst = ADMPTSGROUPNAMENULL;
1504         goto fail_pts_GroupMemberRemove;
1505     }
1506
1507     if (!TranslateTwoNames
1508         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
1509          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
1510         goto fail_pts_GroupMemberRemove;
1511     }
1512
1513     /*
1514      * Make the rpc
1515      */
1516
1517     tst =
1518         ubik_PR_RemoveFromGroup(c_handle->pts, 0, ids.idlist_val[0],
1519                   ids.idlist_val[1]);
1520
1521     if (tst != 0) {
1522         goto fail_pts_GroupMemberRemove;
1523     }
1524     rc = 1;
1525
1526   fail_pts_GroupMemberRemove:
1527
1528     if (ids.idlist_val != 0) {
1529         free(ids.idlist_val);
1530     }
1531
1532     if (st != NULL) {
1533         *st = tst;
1534     }
1535     return rc;
1536 }
1537
1538 /*
1539  * pts_GroupRename - change the name of a group
1540  *
1541  * PARAMETERS
1542  *
1543  * IN cellHandle - a previously opened cellHandle that corresponds
1544  * to the cell where the group exists.
1545  *
1546  * IN oldName - the current group name
1547  *
1548  * IN newName - the new group name
1549  *
1550  * LOCKS
1551  *
1552  * No locks are held by this function
1553  *
1554  * RETURN CODES
1555  *
1556  * Returns != 0 upon successful completion.
1557  *
1558  */
1559
1560 int ADMINAPI
1561 pts_GroupRename(const void *cellHandle, const char *oldName,
1562                 char *newName, afs_status_p st)
1563 {
1564     int rc = 0;
1565     afs_status_t tst = 0;
1566     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1567     afs_int32 groupId = 0;
1568
1569     /*
1570      * Validate arguments
1571      */
1572
1573     if (!IsValidCellHandle(c_handle, &tst)) {
1574         goto fail_pts_GroupRename;
1575     }
1576
1577     if ((newName == NULL) || (*newName == 0)) {
1578         tst = ADMPTSNEWNAMENULL;
1579         goto fail_pts_GroupRename;
1580     }
1581
1582     if ((oldName == NULL) || (*oldName == 0)) {
1583         tst = ADMPTSOLDNAMENULL;
1584         goto fail_pts_GroupRename;
1585     }
1586
1587     /*
1588      * Translate the group name into an id.
1589      */
1590
1591     if (!TranslateOneName
1592         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &groupId, &tst)) {
1593         goto fail_pts_GroupRename;
1594     }
1595
1596     /*
1597      * Make the rpc
1598      */
1599
1600     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, groupId, newName, 0, 0);
1601
1602     if (tst != 0) {
1603         goto fail_pts_GroupRename;
1604     }
1605     rc = 1;
1606
1607   fail_pts_GroupRename:
1608
1609     if (st != NULL) {
1610         *st = tst;
1611     }
1612     return rc;
1613 }
1614
1615 /*
1616  * SetGroupAccess - translate our Access notation to pts flags.
1617  *
1618  * PARAMETERS
1619  *
1620  * IN rights - the permissions.
1621  *
1622  * OUT flags - a pointer to an afs_int32 structure that 
1623  * contains the flags to pass to pts.
1624  *
1625  * LOCKS
1626  *
1627  * No locks are held by this function
1628  *
1629  * RETURN CODES
1630  *
1631  * Returns != 0 upon successful completion.
1632  *
1633  */
1634
1635 static int
1636 SetGroupAccess(const pts_GroupUpdateEntry_p rights, afs_int32 * flags,
1637                afs_status_p st)
1638 {
1639     int rc = 0;
1640     afs_status_t tst = 0;
1641
1642     *flags = 0;
1643
1644     if (rights->listDelete == PTS_GROUP_ACCESS) {
1645         *flags |= 1;
1646     } else if (rights->listDelete == PTS_GROUP_ANYUSER_ACCESS) {
1647         tst = ADMPTSINVALIDGROUPDELETEPERM;
1648         goto fail_SetGroupAccess;
1649     }
1650
1651     if (rights->listAdd == PTS_GROUP_ACCESS) {
1652         *flags |= 2;
1653     } else if (rights->listAdd == PTS_GROUP_ANYUSER_ACCESS) {
1654         *flags |= 4;
1655     }
1656
1657     if (rights->listMembership == PTS_GROUP_ACCESS) {
1658         *flags |= 8;
1659     } else if (rights->listMembership == PTS_GROUP_ANYUSER_ACCESS) {
1660         *flags |= 16;
1661     }
1662
1663     if (rights->listGroupsOwned == PTS_GROUP_ANYUSER_ACCESS) {
1664         *flags |= 32;
1665     } else if (rights->listGroupsOwned == PTS_GROUP_ACCESS) {
1666         tst = ADMPTSINVALIDGROUPSOWNEDPERM;
1667         goto fail_SetGroupAccess;
1668     }
1669
1670     if (rights->listStatus == PTS_GROUP_ACCESS) {
1671         *flags |= 64;
1672     } else if (rights->listStatus == PTS_GROUP_ANYUSER_ACCESS) {
1673         *flags |= 128;
1674     }
1675     rc = 1;
1676
1677   fail_SetGroupAccess:
1678
1679     if (st != NULL) {
1680         *st = tst;
1681     }
1682     return rc;
1683 }
1684
1685 /*
1686  * pts_GroupModify - change the contents of a group entry.
1687  *
1688  * PARAMETERS
1689  *
1690  * IN cellHandle - a previously opened cellHandle that corresponds
1691  * to the cell where the group exists.
1692  *
1693  * IN groupName - the group to change
1694  *
1695  * OUT newEntryP - a pointer to a pts_GroupUpdateEntry_t structure that 
1696  * contains the new information for the group.
1697  *
1698  * LOCKS
1699  *
1700  * No locks are held by this function
1701  *
1702  * RETURN CODES
1703  *
1704  * Returns != 0 upon successful completion.
1705  *
1706  */
1707
1708 int ADMINAPI
1709 pts_GroupModify(const void *cellHandle, const char *groupName,
1710                 const pts_GroupUpdateEntry_p newEntryP, afs_status_p st)
1711 {
1712     int rc = 0;
1713     afs_status_t tst = 0;
1714     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1715     afs_int32 groupId = 0;
1716     afs_int32 flags = 0;
1717
1718     /*
1719      * Validate arguments
1720      */
1721
1722     if (!IsValidCellHandle(c_handle, &tst)) {
1723         goto fail_pts_GroupModify;
1724     }
1725
1726     if ((groupName == NULL) || (*groupName == 0)) {
1727         tst = ADMPTSGROUPNAMENULL;
1728         goto fail_pts_GroupModify;
1729     }
1730
1731
1732     if (newEntryP == NULL) {
1733         tst = ADMPTSNEWENTRYPNULL;
1734         goto fail_pts_GroupModify;
1735     }
1736
1737     /*
1738      * Translate the group name into an id.
1739      */
1740
1741     if (!TranslateOneName
1742         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1743         goto fail_pts_GroupModify;
1744     }
1745
1746     /*
1747      * Set the flags argument
1748      */
1749
1750     if (!SetGroupAccess(newEntryP, &flags, &tst)) {
1751         goto fail_pts_GroupModify;
1752     }
1753
1754     /*
1755      * Make the rpc
1756      */
1757
1758     tst =
1759         ubik_PR_SetFieldsEntry(c_handle->pts, 0, groupId, PR_SF_ALLBITS,
1760                   flags, 0, 0, 0, 0);
1761
1762     if (tst != 0) {
1763         goto fail_pts_GroupModify;
1764     }
1765     rc = 1;
1766
1767   fail_pts_GroupModify:
1768
1769     if (st != NULL) {
1770         *st = tst;
1771     }
1772     return rc;
1773 }
1774
1775 /*
1776  * pts_UserCreate - create a new user.
1777  *
1778  * PARAMETERS
1779  *
1780  * IN cellHandle - a previously opened cellHandle that corresponds
1781  * to the cell where the group exists.
1782  *
1783  * IN newUser - the name of the new user.
1784  *
1785  * IN newUserId - the id to assign to the new user.  Pass 0 to have the
1786  * id assigned by pts.
1787  *
1788  * LOCKS
1789  *
1790  * No locks are held by this function
1791  *
1792  * RETURN CODES
1793  *
1794  * Returns != 0 upon successful completion.
1795  *
1796  */
1797
1798 int ADMINAPI
1799 pts_UserCreate(const void *cellHandle, char *userName, int *newUserId,
1800                afs_status_p st)
1801 {
1802     int rc = 0;
1803     afs_status_t tst = 0;
1804     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1805
1806     /*
1807      * Validate arguments
1808      */
1809
1810     if (!IsValidCellHandle(c_handle, &tst)) {
1811         goto fail_pts_UserCreate;
1812     }
1813
1814     if ((userName == NULL) || (*userName == 0)) {
1815         tst = ADMPTSUSERNAMENULL;
1816         goto fail_pts_UserCreate;
1817     }
1818
1819     if (newUserId == NULL) {
1820         tst = ADMPTSNEWUSERIDNULL;
1821         goto fail_pts_UserCreate;
1822     }
1823
1824     /*
1825      * We make a different rpc based upon the input to this function
1826      */
1827
1828     if (*newUserId != 0) {
1829         tst =
1830             ubik_PR_INewEntry(c_handle->pts, 0, userName, *newUserId,
1831                       0);
1832     } else {
1833         tst =
1834             ubik_PR_NewEntry(c_handle->pts, 0, userName, 0, 0,
1835                       newUserId);
1836     }
1837
1838     if (tst != 0) {
1839         goto fail_pts_UserCreate;
1840     }
1841     rc = 1;
1842
1843   fail_pts_UserCreate:
1844
1845     if (st != NULL) {
1846         *st = tst;
1847     }
1848     return rc;
1849 }
1850
1851 /*
1852  * pts_UserDelete - delete a user.
1853  *
1854  * PARAMETERS
1855  *
1856  * IN cellHandle - a previously opened cellHandle that corresponds
1857  * to the cell where the group exists.
1858  *
1859  * IN user - the name of the user to delete.
1860  *
1861  * LOCKS
1862  *
1863  * No locks are held by this function
1864  *
1865  * RETURN CODES
1866  *
1867  * Returns != 0 upon successful completion.
1868  *
1869  */
1870
1871 int ADMINAPI
1872 pts_UserDelete(const void *cellHandle, const char *userName, afs_status_p st)
1873 {
1874     return EntryDelete(cellHandle, userName, ADMPTSUSERNAMENULL,
1875                        ADMPTSUSERNAMETOOLONG, st);
1876 }
1877
1878
1879 /*
1880  * GetUserAccess - a small convenience function for setting
1881  * permissions.
1882  *
1883  * PARAMETERS
1884  *
1885  * IN access - a pointer to a pts_userAccess_t to be set with the
1886  * correct permission.
1887  *
1888  * IN flag - the current permission flag used to derive the permission.
1889  *
1890  * LOCKS
1891  *
1892  * No locks are obtained or released by this function
1893  *
1894  * RETURN CODES
1895  *
1896  * Since this function cannot fail, it returns void.
1897  *
1898  */
1899
1900 static void
1901 GetUserAccess(pts_userAccess_p access, afs_int32 flag)
1902 {
1903
1904     *access = PTS_USER_OWNER_ACCESS;
1905     if (flag == 2) {
1906         *access = PTS_USER_ANYUSER_ACCESS;
1907     }
1908 }
1909
1910 /*
1911  * IsAdministrator - determine if a user is an administrator.
1912  *
1913  * PARAMETERS
1914  *
1915  * IN cellHandle - a previously opened cellHandle that corresponds
1916  * to the cell where the group exists.
1917  *
1918  * IN userEntry - the user data for the user in question.
1919  *
1920  * OUT admin - set to 1 if the user is an administrator, 0 otherwise.
1921  *
1922  * LOCKS
1923  *
1924  * No locks are held by this function
1925  *
1926  * RETURN CODES
1927  *
1928  * Returns != 0 upon successful completion.
1929  *
1930  */
1931
1932 static int
1933 IsAdministrator(const afs_cell_handle_p c_handle, afs_int32 userId,
1934                 int *admin, afs_status_p st)
1935 {
1936     int rc = 0;
1937     afs_status_t tst = 0;
1938     afs_int32 adminId = 0;
1939     afs_int32 isAdmin = 0;
1940
1941     *admin = 0;
1942
1943     if (userId == SYSADMINID) {
1944         *admin = 1;
1945     } else {
1946         if (!TranslateOneName
1947             (c_handle, "system:administrators", ADMPTSGROUPNAMETOOLONG,
1948              &adminId, &tst)) {
1949             goto fail_IsAdministrator;
1950         }
1951         tst =
1952             ubik_PR_IsAMemberOf(c_handle->pts, 0, userId, adminId,
1953                       &isAdmin);
1954         if (tst != 0) {
1955             goto fail_IsAdministrator;
1956         }
1957         if (isAdmin) {
1958             *admin = 1;
1959         }
1960     }
1961     rc = 1;
1962
1963   fail_IsAdministrator:
1964
1965     if (st != NULL) {
1966         *st = tst;
1967     }
1968     return rc;
1969 }
1970
1971 /*
1972  * pts_UserGet - retrieve information about a particular user.
1973  *
1974  * PARAMETERS
1975  *
1976  * IN cellHandle - a previously opened cellHandle that corresponds
1977  * to the cell where the group exists.
1978  *
1979  * IN userName - the name of the user to retrieve.
1980  *
1981  * OUT userP - a pointer to a pts_UserEntry_t that is filled upon successful
1982  * completion.
1983  *
1984  * LOCKS
1985  *
1986  * No locks are held by this function
1987  *
1988  * RETURN CODES
1989  *
1990  * Returns != 0 upon successful completion.
1991  *
1992  */
1993
1994 int ADMINAPI
1995 pts_UserGet(const void *cellHandle, const char *userName,
1996             pts_UserEntry_p userP, afs_status_p st)
1997 {
1998     int rc = 0;
1999     afs_status_t tst = 0;
2000     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2001     struct prcheckentry userEntry;
2002     afs_int32 userId = 0;
2003     afs_int32 flags;
2004     afs_int32 twobit;
2005     idlist ids;
2006     afs_int32 ptsids[2];
2007     namelist names;
2008     int admin = 0;
2009
2010
2011     /*
2012      * Validate arguments
2013      */
2014
2015     if (!IsValidCellHandle(c_handle, &tst)) {
2016         goto fail_pts_UserGet;
2017     }
2018
2019     if ((userName == NULL) || (*userName == 0)) {
2020         tst = ADMPTSUSERNAMENULL;
2021         goto fail_pts_UserGet;
2022     }
2023
2024     if (userP == NULL) {
2025         tst = ADMPTSUSERPNULL;
2026         goto fail_pts_UserGet;
2027     }
2028
2029     /*
2030      * Translate the group name into an id.
2031      */
2032
2033     if (!TranslateOneName
2034         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2035         goto fail_pts_UserGet;
2036     }
2037
2038     /*
2039      * Retrieve information about the group
2040      */
2041
2042     tst = ubik_PR_ListEntry(c_handle->pts, 0, userId, &userEntry);
2043
2044     if (tst != 0) {
2045         goto fail_pts_UserGet;
2046     }
2047
2048     userP->groupMembershipCount = userEntry.count;
2049     userP->groupCreationQuota = userEntry.ngroups;
2050     /*
2051      * The administrator id, or any member of "system:administrators"
2052      * has unlimited group creation quota.  Denote this by setting
2053      * quota to -1.
2054      */
2055
2056     if (!IsAdministrator(c_handle, userEntry.id, &admin, &tst)) {
2057         goto fail_pts_UserGet;
2058     }
2059
2060     if (admin != 0) {
2061         userP->groupCreationQuota = -1;
2062     }
2063
2064     userP->nameUid = userEntry.id;
2065     userP->ownerUid = userEntry.owner;
2066     userP->creatorUid = userEntry.creator;
2067     strncpy(userP->name, userEntry.name, PTS_MAX_NAME_LEN);
2068     userP->name[PTS_MAX_NAME_LEN - 1] = '\0';
2069
2070     /*
2071      * The permission bits are described in the GroupGet function above.
2072      * The user entry only uses 3 of the 5 permissions, so we shift
2073      * past the unused entries.
2074      */
2075
2076     flags = userEntry.flags;
2077     flags = flags >> 3;
2078     twobit = flags & 3;
2079
2080     GetUserAccess(&userP->listMembership, twobit);
2081
2082     flags = flags >> 2;
2083
2084     if (flags & 1) {
2085         userP->listGroupsOwned = PTS_USER_ANYUSER_ACCESS;
2086     } else {
2087         userP->listGroupsOwned = PTS_USER_OWNER_ACCESS;
2088     }
2089
2090     flags = flags >> 1;
2091     twobit = flags & 3;
2092
2093     GetUserAccess(&userP->listStatus, twobit);
2094
2095     /* 
2096      * Make another rpc and translate the owner and creator ids into
2097      * character strings.
2098      */
2099
2100     ids.idlist_len = 2;
2101     ids.idlist_val = ptsids;
2102     ptsids[0] = userEntry.owner;
2103     ptsids[1] = userEntry.creator;
2104     names.namelist_len = 0;
2105     names.namelist_val = 0;
2106
2107
2108     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
2109         goto fail_pts_UserGet;
2110     }
2111
2112     strncpy(userP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
2113     userP->owner[PTS_MAX_NAME_LEN - 1] ='\0';
2114     strncpy(userP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
2115     userP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
2116     free(names.namelist_val);
2117     rc = 1;
2118
2119   fail_pts_UserGet:
2120
2121     if (st != NULL) {
2122         *st = tst;
2123     }
2124     return rc;
2125 }
2126
2127 /*
2128  * pts_UserRename - rename a user.
2129  *
2130  * PARAMETERS
2131  *
2132  * IN cellHandle - a previously opened cellHandle that corresponds
2133  * to the cell where the group exists.
2134  *
2135  * IN oldName - the name of the user to rename.
2136  *
2137  * IN newName - the new user name.
2138  *
2139  * LOCKS
2140  *
2141  * No locks are held by this function
2142  *
2143  * RETURN CODES
2144  *
2145  * Returns != 0 upon successful completion.
2146  *
2147  */
2148
2149 int ADMINAPI
2150 pts_UserRename(const void *cellHandle, const char *oldName,
2151                char *newName, afs_status_p st)
2152 {
2153     int rc = 0;
2154     afs_status_t tst = 0;
2155     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2156     afs_int32 userId = 0;
2157
2158     /*
2159      * Validate arguments
2160      */
2161
2162     if (!IsValidCellHandle(c_handle, &tst)) {
2163         goto fail_pts_UserRename;
2164     }
2165
2166     if ((oldName == NULL) || (*oldName == 0)) {
2167         tst = ADMPTSOLDNAMENULL;
2168         goto fail_pts_UserRename;
2169     }
2170
2171     if ((newName == NULL) || (*newName == 0)) {
2172         tst = ADMPTSNEWNAMENULL;
2173         goto fail_pts_UserRename;
2174     }
2175
2176     /*
2177      * Translate the user name into an id.
2178      */
2179
2180     if (!TranslateOneName
2181         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &userId, &tst)) {
2182         goto fail_pts_UserRename;
2183     }
2184
2185     /*
2186      * Make the rpc
2187      */
2188
2189     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, userId, newName, 0, 0);
2190
2191     if (tst != 0) {
2192         goto fail_pts_UserRename;
2193     }
2194     rc = 1;
2195
2196   fail_pts_UserRename:
2197
2198     if (st != NULL) {
2199         *st = tst;
2200     }
2201     return rc;
2202 }
2203
2204 /*
2205  * SetUserAccess - translate our Access notation to pts flags.
2206  *
2207  * PARAMETERS
2208  *
2209  * IN userP - the user structure that contains the new permissions.
2210  *
2211  * OUT flags - a pointer to an afs_int32 structure that 
2212  * contains the flags to pass to pts.
2213  *
2214  * LOCKS
2215  *
2216  * No locks are held by this function
2217  *
2218  * RETURN CODES
2219  *
2220  * Returns != 0 upon successful completion.
2221  *
2222  */
2223
2224 static int
2225 SetUserAccess(const pts_UserUpdateEntry_p userP, afs_int32 * flags,
2226               afs_status_p st)
2227 {
2228     int rc = 0;
2229     afs_status_t tst = 0;
2230
2231     *flags = 0;
2232
2233     if (userP->listMembership == PTS_USER_ANYUSER_ACCESS) {
2234         *flags |= 16;
2235     }
2236
2237     if (userP->listGroupsOwned == PTS_USER_ANYUSER_ACCESS) {
2238         *flags |= 32;
2239     }
2240
2241     if (userP->listStatus == PTS_USER_ANYUSER_ACCESS) {
2242         *flags |= 128;
2243     }
2244     rc = 1;
2245
2246     if (st != NULL) {
2247         *st = tst;
2248     }
2249     return rc;
2250 }
2251
2252
2253 /*
2254  * pts_UserModify - update a user entry.
2255  *
2256  * PARAMETERS
2257  *
2258  * IN cellHandle - a previously opened cellHandle that corresponds
2259  * to the cell where the group exists.
2260  *
2261  * IN userName - the name of the user to update.
2262  *
2263  * IN newEntryP - a pointer to a pts_UserUpdateEntry_t that contains the
2264  * new information for user.
2265  *
2266  * LOCKS
2267  *
2268  * No locks are held by this function
2269  *
2270  * RETURN CODES
2271  *
2272  * Returns != 0 upon successful completion.
2273  *
2274  */
2275
2276 int ADMINAPI
2277 pts_UserModify(const void *cellHandle, const char *userName,
2278                const pts_UserUpdateEntry_p newEntryP, afs_status_p st)
2279 {
2280     int rc = 0;
2281     afs_status_t tst = 0;
2282     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2283     afs_int32 userId = 0;
2284     afs_int32 newQuota = 0;
2285     afs_int32 mask = 0;
2286     afs_int32 flags = 0;
2287
2288     /*
2289      * Validate arguments
2290      */
2291
2292     if (!IsValidCellHandle(c_handle, &tst)) {
2293         goto fail_pts_UserModify;
2294     }
2295
2296     if ((userName == NULL) || (*userName == 0)) {
2297         tst = ADMPTSUSERNAMENULL;
2298         goto fail_pts_UserModify;
2299     }
2300
2301     if (newEntryP == NULL) {
2302         tst = ADMPTSNEWENTRYPNULL;
2303         goto fail_pts_UserModify;
2304     }
2305
2306     /*
2307      * Translate the user name into an id.
2308      */
2309
2310     if (!TranslateOneName
2311         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2312         goto fail_pts_UserModify;
2313     }
2314
2315
2316     if (newEntryP->flag & PTS_USER_UPDATE_GROUP_CREATE_QUOTA) {
2317         mask |= PR_SF_NGROUPS;
2318         newQuota = newEntryP->groupCreationQuota;
2319     }
2320
2321     if (newEntryP->flag & PTS_USER_UPDATE_PERMISSIONS) {
2322         mask |= PR_SF_ALLBITS;
2323         if (!SetUserAccess(newEntryP, &flags, &tst)) {
2324             goto fail_pts_UserModify;
2325         }
2326     }
2327
2328     /*
2329      * Make the rpc
2330      */
2331
2332     tst =
2333         ubik_PR_SetFieldsEntry(c_handle->pts, 0, userId, mask, flags,
2334                   newQuota, 0, 0, 0);
2335
2336     if (tst != 0) {
2337         goto fail_pts_UserModify;
2338     }
2339     rc = 1;
2340
2341   fail_pts_UserModify:
2342
2343     if (st != NULL) {
2344         *st = tst;
2345     }
2346     return rc;
2347 }
2348
2349 /*
2350  * pts_UserMaxGet - get the maximum in use user id.
2351  *
2352  * PARAMETERS
2353  *
2354  * IN cellHandle - a previously opened cellHandle that corresponds
2355  * to the cell where the group exists.
2356  *
2357  * OUT maxUserId - upon successful completion contains the max in use id.
2358  *
2359  * LOCKS
2360  *
2361  * No locks are held by this function
2362  *
2363  * RETURN CODES
2364  *
2365  * Returns != 0 upon successful completion.
2366  *
2367  */
2368
2369 int ADMINAPI
2370 pts_UserMaxGet(const void *cellHandle, int *maxUserId, afs_status_p st)
2371 {
2372     int rc = 0;
2373     afs_status_t tst = 0;
2374     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2375     afs_int32 maxGroupId = 0;
2376
2377     /*
2378      * Validate arguments
2379      */
2380
2381     if (!IsValidCellHandle(c_handle, &tst)) {
2382         goto fail_pts_UserMaxGet;
2383     }
2384
2385     if (maxUserId == NULL) {
2386         tst = ADMPTSMAXUSERIDNULL;
2387         goto fail_pts_UserMaxGet;
2388     }
2389
2390     tst = ubik_PR_ListMax(c_handle->pts, 0, maxUserId, &maxGroupId);
2391
2392     if (tst != 0) {
2393         goto fail_pts_UserMaxGet;
2394     }
2395     rc = 1;
2396
2397   fail_pts_UserMaxGet:
2398
2399     if (st != NULL) {
2400         *st = tst;
2401     }
2402     return rc;
2403 }
2404
2405 /*
2406  * pts_UserMaxSet - set the maximum user id.
2407  *
2408  * PARAMETERS
2409  *
2410  * IN cellHandle - a previously opened cellHandle that corresponds
2411  * to the cell where the group exists.
2412  *
2413  * IN maxUserId - the new max user id.
2414  *
2415  * LOCKS
2416  *
2417  * No locks are held by this function
2418  *
2419  * RETURN CODES
2420  *
2421  * Returns != 0 upon successful completion.
2422  *
2423  */
2424
2425 int ADMINAPI
2426 pts_UserMaxSet(const void *cellHandle, int maxUserId, afs_status_p st)
2427 {
2428     int rc = 0;
2429     afs_status_t tst = 0;
2430     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2431
2432     /*
2433      * Validate arguments
2434      */
2435
2436     if (!IsValidCellHandle(c_handle, &tst)) {
2437         goto fail_pts_UserMaxSet;
2438     }
2439
2440     tst = ubik_PR_SetMax(c_handle->pts, 0, maxUserId, 0);
2441
2442     if (tst != 0) {
2443         goto fail_pts_UserMaxSet;
2444     }
2445     rc = 1;
2446
2447   fail_pts_UserMaxSet:
2448
2449     if (st != NULL) {
2450         *st = tst;
2451     }
2452     return rc;
2453 }
2454
2455 /*
2456  * pts_UserMemberListBegin - begin iterating over the list of groups
2457  * a particular user belongs to.
2458  *
2459  * PARAMETERS
2460  *
2461  * IN cellHandle - a previously opened cellHandle that corresponds
2462  * to the cell where the group exists.
2463  *
2464  * IN groupName - the group whose members will be returned.
2465  *
2466  * OUT iterationIdP - upon successful completion contains a iterator that
2467  * can be passed to pts_GroupMemberListNext.
2468  *
2469  * LOCKS
2470  *
2471  * No locks are obtained or released by this function
2472  *
2473  * RETURN CODES
2474  *
2475  * Returns != 0 upon successful completion.
2476  *
2477  */
2478
2479 int ADMINAPI
2480 pts_UserMemberListBegin(const void *cellHandle, const char *userName,
2481                         void **iterationIdP, afs_status_p st)
2482 {
2483     return MemberListBegin(cellHandle, userName, ADMPTSUSERNAMENULL,
2484                            ADMPTSUSERNAMETOOLONG, iterationIdP, st);
2485
2486 }
2487
2488 /*
2489  * pts_UserMemberListNext - get the next group a user belongs to
2490  *
2491  * PARAMETERS
2492  *
2493  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2494  *
2495  * OUT userName - upon successful completion contains the next group a user
2496  * belongs to.
2497  *
2498  * LOCKS
2499  *
2500  * The iterator mutex is held during the retrieval of the next member.
2501  *
2502  * RETURN CODES
2503  *
2504  * Returns != 0 upon successful completion.
2505  *
2506  */
2507
2508 int ADMINAPI
2509 pts_UserMemberListNext(const void *iterationId, char *userName,
2510                        afs_status_p st)
2511 {
2512     return pts_GroupMemberListNext(iterationId, userName, st);
2513 }
2514
2515 /*
2516  * pts_UserMemberListDone - finish using a user list iterator
2517  *
2518  * PARAMETERS
2519  *
2520  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2521  *
2522  * LOCKS
2523  *
2524  * The iterator is locked and then destroyed
2525  *
2526  * RETURN CODES
2527  *
2528  * Returns != 0 upon successful completion.
2529  *
2530  * ASSUMPTIONS
2531  *
2532  * It is the user's responsibility to make sure pts_UserMemberListDone
2533  * is called only once for each iterator.
2534  */
2535
2536 int ADMINAPI
2537 pts_UserMemberListDone(const void *iterationId, afs_status_p st)
2538 {
2539     return pts_GroupMemberListDone(iterationId, st);
2540 }
2541
2542 typedef struct owned_group_list {
2543     namelist owned_names;       /* the list of character names owned by this id */
2544     prlist owned_ids;           /* the list of pts ids owned by this id */
2545     afs_int32 index;            /* the index into owned_names for the next group */
2546     afs_int32 owner;            /* the pts id of the owner */
2547     afs_int32 more;             /* the last parameter to PR_ListOwned */
2548     int finished_retrieving;    /* set when we've processed the last owned_names */
2549     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2550     char group[CACHED_ITEMS][PTS_MAX_NAME_LEN]; /* cache of names */
2551 } owned_group_list_t, *owned_group_list_p;
2552
2553 static int
2554 DeleteOwnedGroupSpecificData(void *rpc_specific, afs_status_p st)
2555 {
2556     int rc = 0;
2557     afs_status_t tst = 0;
2558     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2559
2560     if (list->owned_names.namelist_val != NULL) {
2561         free(list->owned_names.namelist_val);
2562     }
2563
2564     if (list->owned_ids.prlist_val != NULL) {
2565         free(list->owned_ids.prlist_val);
2566     }
2567     rc = 1;
2568
2569     if (st != NULL) {
2570         *st = tst;
2571     }
2572     return rc;
2573 }
2574
2575 static int
2576 GetOwnedGroupRPC(void *rpc_specific, int slot, int *last_item,
2577                  int *last_item_contains_data, afs_status_p st)
2578 {
2579     int rc = 0;
2580     afs_status_t tst = 0;
2581     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2582
2583     /*
2584      * We really don't make an rpc for every entry we return here
2585      * since the pts interface allows several members to be returned
2586      * with one rpc, but we fake it to make the iterator happy.
2587      */
2588
2589     /*
2590      * Check to see if we are done retrieving data
2591      */
2592
2593     if ((list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2594         *last_item = 1;
2595         *last_item_contains_data = 0;
2596         goto fail_GetOwnedGroupRPC;
2597     }
2598
2599     /*
2600      * Check to see if we really need to make an rpc
2601      */
2602
2603     if ((!list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2604         tst =
2605             ubik_PR_ListOwned(list->c_handle->pts, 0, list->owner,
2606                       &list->owned_ids, &list->more);
2607         if (tst != 0) {
2608             goto fail_GetOwnedGroupRPC;
2609         }
2610
2611         if (!TranslatePTSIds
2612             (list->c_handle, &list->owned_names, (idlist *) & list->owned_ids,
2613              &tst)) {
2614             goto fail_GetOwnedGroupRPC;
2615         }
2616         list->index = 0;
2617
2618         if (list->owned_names.namelist_val == NULL) {
2619             *last_item = 1;
2620             *last_item_contains_data = 0;
2621             goto fail_GetOwnedGroupRPC;
2622         }
2623     }
2624
2625     /*
2626      * We can retrieve the next group from data we already received
2627      */
2628
2629     strcpy(list->group[slot], list->owned_names.namelist_val[list->index]);
2630     list->index++;
2631
2632     /*
2633      * Check to see if there is more data to be retrieved
2634      * We need to free up the previously retrieved data here
2635      * and then check to see if the last rpc indicated that there
2636      * were more items to retrieve.
2637      */
2638
2639     if (list->index >= list->owned_names.namelist_len) {
2640         list->owned_names.namelist_len = 0;
2641         free(list->owned_names.namelist_val);
2642         list->owned_names.namelist_val = 0;
2643
2644         list->owned_ids.prlist_len = 0;
2645         free(list->owned_ids.prlist_val);
2646         list->owned_ids.prlist_val = 0;
2647
2648         if (!list->more) {
2649             list->finished_retrieving = 1;
2650         }
2651     }
2652     rc = 1;
2653
2654   fail_GetOwnedGroupRPC:
2655
2656     if (st != NULL) {
2657         *st = tst;
2658     }
2659     return rc;
2660 }
2661
2662 static int
2663 GetOwnedGroupFromCache(void *rpc_specific, int slot, void *dest,
2664                        afs_status_p st)
2665 {
2666     int rc = 0;
2667     afs_status_t tst = 0;
2668     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2669
2670     strcpy((char *)dest, list->group[slot]);
2671     rc = 1;
2672
2673     if (st != NULL) {
2674         *st = tst;
2675     }
2676
2677     return rc;
2678 }
2679
2680 /*
2681  * pts_OwnedGroupListBegin - begin iterating over the list of groups
2682  * a particular user owns.
2683  *
2684  * PARAMETERS
2685  *
2686  * IN cellHandle - a previously opened cellHandle that corresponds
2687  * to the cell where the group exists.
2688  *
2689  * IN ownerName - the owner of the groups of interest.
2690  *
2691  * OUT iterationIdP - upon successful completion contains a iterator that
2692  * can be passed to pts_OwnedGroupListNext.
2693  *
2694  * LOCKS
2695  *
2696  * No locks are held by this function
2697  *
2698  * RETURN CODES
2699  *
2700  * Returns != 0 upon successful completion.
2701  *
2702  */
2703
2704 int ADMINAPI
2705 pts_OwnedGroupListBegin(const void *cellHandle, const char *userName,
2706                         void **iterationIdP, afs_status_p st)
2707 {
2708     int rc = 0;
2709     afs_status_t tst = 0;
2710     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2711     afs_admin_iterator_p iter = malloc(sizeof(afs_admin_iterator_t));
2712     owned_group_list_p list = malloc(sizeof(owned_group_list_t));
2713
2714     /*
2715      * Validate arguments
2716      */
2717
2718     if (!IsValidCellHandle(c_handle, &tst)) {
2719         goto fail_pts_OwnedGroupListBegin;
2720     }
2721
2722     if ((userName == NULL) || (*userName == 0)) {
2723         tst = ADMPTSUSERNAMENULL;
2724         goto fail_pts_OwnedGroupListBegin;
2725     }
2726
2727     if (iterationIdP == NULL) {
2728         tst = ADMITERATORNULL;
2729         goto fail_pts_OwnedGroupListBegin;
2730     }
2731
2732     if ((iter == NULL) || (list == NULL)) {
2733         tst = ADMNOMEM;
2734         goto fail_pts_OwnedGroupListBegin;
2735     }
2736
2737     /*
2738      * Initialize the iterator specific data
2739      */
2740
2741     list->index = 0;
2742     list->finished_retrieving = 0;
2743     list->c_handle = c_handle;
2744     list->owned_names.namelist_len = 0;
2745     list->owned_names.namelist_val = 0;
2746     list->owned_ids.prlist_len = 0;
2747     list->owned_ids.prlist_val = 0;
2748
2749     /*
2750      * Translate the user name into an id.
2751      */
2752
2753     if (!TranslateOneName
2754         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &list->owner, &tst)) {
2755         goto fail_pts_OwnedGroupListBegin;
2756     }
2757
2758     if (IteratorInit
2759         (iter, (void *)list, GetOwnedGroupRPC, GetOwnedGroupFromCache, NULL,
2760          DeleteOwnedGroupSpecificData, &tst)) {
2761         *iterationIdP = (void *)iter;
2762         rc = 1;
2763     }
2764
2765   fail_pts_OwnedGroupListBegin:
2766
2767     if (rc == 0) {
2768         if (iter != NULL) {
2769             free(iter);
2770         }
2771         if (list != NULL) {
2772             free(list);
2773         }
2774     }
2775
2776     if (st != NULL) {
2777         *st = tst;
2778     }
2779     return rc;
2780 }
2781
2782 /*
2783  * pts_OwnedGroupListNext - get the next group a user owns.
2784  *
2785  * PARAMETERS
2786  *
2787  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2788  *
2789  * OUT groupName - upon successful completion contains the next group a user
2790  * owns.
2791  *
2792  * LOCKS
2793  *
2794  * The iterator mutex is held during the retrieval of the next member.
2795  *
2796  * RETURN CODES
2797  *
2798  * Returns != 0 upon successful completion.
2799  *
2800  */
2801
2802 int ADMINAPI
2803 pts_OwnedGroupListNext(const void *iterationId, char *groupName,
2804                        afs_status_p st)
2805 {
2806     int rc = 0;
2807     afs_status_t tst = 0;
2808     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2809
2810     /*
2811      * Validate arguments
2812      */
2813
2814     if (iterationId == NULL) {
2815         tst = ADMITERATORNULL;
2816         goto fail_pts_OwnedGroupListNext;
2817     }
2818
2819     if (groupName == NULL) {
2820         tst = ADMPTSGROUPNAMENULL;
2821         goto fail_pts_OwnedGroupListNext;
2822     }
2823
2824     rc = IteratorNext(iter, (void *)groupName, &tst);
2825
2826   fail_pts_OwnedGroupListNext:
2827
2828     if (st != NULL) {
2829         *st = tst;
2830     }
2831     return rc;
2832 }
2833
2834 /*
2835  * pts_OwnedGroupListDone - finish using a group list iterator
2836  *
2837  * PARAMETERS
2838  *
2839  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2840  *
2841  * LOCKS
2842  *
2843  * The iterator is locked and then destroyed
2844  *
2845  * RETURN CODES
2846  *
2847  * Returns != 0 upon successful completion.
2848  *
2849  * ASSUMPTIONS
2850  *
2851  * It is the user's responsibility to make sure pts_OwnedGroupListDone
2852  * is called only once for each iterator.
2853  */
2854
2855 int ADMINAPI
2856 pts_OwnedGroupListDone(const void *iterationId, afs_status_p st)
2857 {
2858     int rc = 0;
2859     afs_status_t tst = 0;
2860     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2861
2862     /*
2863      * Validate arguments
2864      */
2865
2866     if (iterationId == NULL) {
2867         tst = ADMITERATORNULL;
2868         goto fail_pts_OwnedGroupListDone;
2869     }
2870
2871     rc = IteratorDone(iter, &tst);
2872
2873   fail_pts_OwnedGroupListDone:
2874
2875     if (st != NULL) {
2876         *st = tst;
2877     }
2878     return rc;
2879 }
2880
2881 typedef struct pts_list {
2882     prlistentries *names;       /* the current list of pts names in this cell */
2883     prlistentries *currName;    /* the current pts entry */
2884     afs_int32 index;            /* the index into names for the next pts entry */
2885     afs_int32 nextstartindex;   /* the next start index for the RPC */
2886     afs_int32 nentries;         /* the number of entries in names */
2887     afs_int32 flag;             /* the type of the list */
2888     int finished_retrieving;    /* set when we've processed the last owned_names */
2889     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2890     char entries[CACHED_ITEMS][PTS_MAX_NAME_LEN];       /* cache of pts names */
2891 } pts_list_t, *pts_list_p;
2892
2893 static int
2894 DeletePTSSpecificData(void *rpc_specific, afs_status_p st)
2895 {
2896     int rc = 0;
2897     afs_status_t tst = 0;
2898     pts_list_p list = (pts_list_p) rpc_specific;
2899
2900     if (list->names) {
2901         free(list->names);
2902     }
2903
2904     rc = 1;
2905
2906     if (st != NULL) {
2907         *st = tst;
2908     }
2909     return rc;
2910 }
2911
2912 static int
2913 GetPTSRPC(void *rpc_specific, int slot, int *last_item,
2914           int *last_item_contains_data, afs_status_p st)
2915 {
2916     int rc = 0;
2917     afs_status_t tst = 0;
2918     pts_list_p list = (pts_list_p) rpc_specific;
2919
2920     /*
2921      * We really don't make an rpc for every entry we return here
2922      * since the pts interface allows several members to be returned
2923      * with one rpc, but we fake it to make the iterator happy.
2924      */
2925
2926     /*
2927      * Check to see if we are done retrieving data
2928      */
2929
2930     if (list->finished_retrieving) {
2931         *last_item = 1;
2932         *last_item_contains_data = 0;
2933         goto fail_GetPTSRPC;
2934     }
2935
2936     /*
2937      * Check to see if we really need to make an rpc
2938      */
2939
2940     if ((!list->finished_retrieving) && (list->index >= list->nentries)) {
2941         afs_int32 start = list->nextstartindex;
2942         prentries bulkentries;
2943         list->nextstartindex = -1;
2944         bulkentries.prentries_val = 0;
2945         bulkentries.prentries_len = 0;
2946
2947         tst =
2948             ubik_PR_ListEntries(list->c_handle->pts, 0, list->flag,
2949                       start, &bulkentries, &(list->nextstartindex));
2950
2951         if (tst != 0) {
2952             goto fail_GetPTSRPC;
2953         }
2954
2955         list->nentries = bulkentries.prentries_len;
2956         list->names = bulkentries.prentries_val;
2957
2958         list->index = 0;
2959         list->currName = list->names;
2960
2961     }
2962
2963     /*
2964      * We can retrieve the next entry from data we already received
2965      */
2966
2967     strcpy(list->entries[slot], list->currName->name);
2968     list->index++;
2969     list->currName++;
2970
2971
2972     /*
2973      * Check to see if there is more data to be retrieved
2974      * We need to free up the previously retrieved data here
2975      * and then check to see if the last rpc indicated that there
2976      * were more items to retrieve.
2977      */
2978
2979     if (list->index >= list->nentries) {
2980         if (list->names) {
2981             free(list->names);
2982         }
2983         list->names = NULL;
2984
2985         if (list->nextstartindex == -1) {
2986             list->finished_retrieving = 1;
2987         }
2988     }
2989     rc = 1;
2990
2991   fail_GetPTSRPC:
2992
2993     if (st != NULL) {
2994         *st = tst;
2995     }
2996
2997     return rc;
2998 }
2999
3000 static int
3001 GetPTSFromCache(void *rpc_specific, int slot, void *dest, afs_status_p st)
3002 {
3003     int rc = 0;
3004     afs_status_t tst = 0;
3005     pts_list_p list = (pts_list_p) rpc_specific;
3006
3007     strcpy((char *)dest, list->entries[slot]);
3008     rc = 1;
3009
3010     if (st != NULL) {
3011         *st = tst;
3012     }
3013
3014     return rc;
3015 }
3016
3017 /*
3018  * pts_UserListBegin - begin iterating over the list of users
3019  * in a particular cell
3020  *
3021  * PARAMETERS
3022  *
3023  * IN cellHandle - a previously opened cellHandle that corresponds
3024  * to the cell where the users exist.
3025  *
3026  * OUT iterationIdP - upon successful completion contains a iterator that
3027  * can be passed to pts_UserListNext.
3028  *
3029  * LOCKS
3030  *
3031  * No locks are held by this function
3032  *
3033  * RETURN CODES
3034  *
3035  * Returns != 0 upon successful completion.
3036  *
3037  */
3038
3039 int ADMINAPI
3040 pts_UserListBegin(const void *cellHandle, void **iterationIdP,
3041                   afs_status_p st)
3042 {
3043     int rc = 0;
3044     afs_status_t tst = 0;
3045     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3046     afs_admin_iterator_p iter = malloc(sizeof(afs_admin_iterator_t));
3047     pts_list_p list = malloc(sizeof(pts_list_t));
3048
3049     /*
3050      * Validate arguments
3051      */
3052
3053     if (!IsValidCellHandle(c_handle, &tst)) {
3054         goto fail_pts_UserListBegin;
3055     }
3056
3057     if (iterationIdP == NULL) {
3058         tst = ADMITERATORNULL;
3059         goto fail_pts_UserListBegin;
3060     }
3061
3062     if ((iter == NULL) || (list == NULL)) {
3063         tst = ADMNOMEM;
3064         goto fail_pts_UserListBegin;
3065     }
3066
3067     /*
3068      * Initialize the iterator specific data
3069      */
3070
3071     list->index = 0;
3072     list->finished_retrieving = 0;
3073     list->c_handle = c_handle;
3074     list->names = NULL;
3075     list->nextstartindex = 0;
3076     list->nentries = 0;
3077     list->flag = PRUSERS;
3078
3079     if (IteratorInit
3080         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3081          DeletePTSSpecificData, &tst)) {
3082         *iterationIdP = (void *)iter;
3083         rc = 1;
3084     }
3085
3086   fail_pts_UserListBegin:
3087
3088     if (rc == 0) {
3089         if (iter != NULL) {
3090             free(iter);
3091         }
3092         if (list != NULL) {
3093             free(list);
3094         }
3095     }
3096
3097     if (st != NULL) {
3098         *st = tst;
3099     }
3100     return rc;
3101 }
3102
3103 /*
3104  * pts_UserListNext - get the next user in the cell.
3105  *
3106  * PARAMETERS
3107  *
3108  * IN iterationId - an iterator previously returned by pts_UserListBegin
3109  *
3110  * OUT groupName - upon successful completion contains the next user
3111  *
3112  * LOCKS
3113  *
3114  * The iterator mutex is held during the retrieval of the next member.
3115  *
3116  * RETURN CODES
3117  *
3118  * Returns != 0 upon successful completion.
3119  *
3120  */
3121
3122 int ADMINAPI
3123 pts_UserListNext(const void *iterationId, char *userName, afs_status_p st)
3124 {
3125     int rc = 0;
3126     afs_status_t tst = 0;
3127     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3128
3129     /*
3130      * Validate arguments
3131      */
3132
3133     if (iterationId == NULL) {
3134         tst = ADMITERATORNULL;
3135         goto fail_pts_UserListNext;
3136     }
3137
3138     if (userName == NULL) {
3139         tst = ADMPTSUSERNAMENULL;
3140         goto fail_pts_UserListNext;
3141     }
3142
3143     rc = IteratorNext(iter, (void *)userName, &tst);
3144
3145   fail_pts_UserListNext:
3146
3147     if (st != NULL) {
3148         *st = tst;
3149     }
3150     return rc;
3151 }
3152
3153 /*
3154  * pts_UserListDone - finish using a user list iterator
3155  *
3156  * PARAMETERS
3157  *
3158  * IN iterationId - an iterator previously returned by pts_UserListBegin
3159  *
3160  * LOCKS
3161  *
3162  * The iterator is locked and then destroyed
3163  *
3164  * RETURN CODES
3165  *
3166  * Returns != 0 upon successful completion.
3167  *
3168  * ASSUMPTIONS
3169  *
3170  * It is the user's responsibility to make sure pts_UserListDone
3171  * is called only once for each iterator.
3172  */
3173
3174 int ADMINAPI
3175 pts_UserListDone(const void *iterationId, afs_status_p st)
3176 {
3177     int rc = 0;
3178     afs_status_t tst = 0;
3179     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3180
3181     /*
3182      * Validate arguments
3183      */
3184
3185     if (iterationId == NULL) {
3186         tst = ADMITERATORNULL;
3187         goto fail_pts_UserListDone;
3188     }
3189
3190     rc = IteratorDone(iter, &tst);
3191
3192   fail_pts_UserListDone:
3193
3194     if (st != NULL) {
3195         *st = tst;
3196     }
3197     return rc;
3198 }
3199
3200 /*
3201  * pts_GroupListBegin - begin iterating over the list of groups
3202  * in a particular cell.
3203  *
3204  * PARAMETERS
3205  *
3206  * IN cellHandle - a previously opened cellHandle that corresponds
3207  * to the cell where the groups exist.
3208  *
3209  * OUT iterationIdP - upon successful completion contains a iterator that
3210  * can be passed to pts_GroupListNext.
3211  *
3212  * LOCKS
3213  *
3214  * No locks are held by this function
3215  *
3216  * RETURN CODES
3217  *
3218  * Returns != 0 upon successful completion.
3219  *
3220  */
3221
3222 int ADMINAPI
3223 pts_GroupListBegin(const void *cellHandle, void **iterationIdP,
3224                    afs_status_p st)
3225 {
3226     int rc = 0;
3227     afs_status_t tst = 0;
3228     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3229     afs_admin_iterator_p iter = malloc(sizeof(afs_admin_iterator_t));
3230     pts_list_p list = malloc(sizeof(pts_list_t));
3231
3232     /*
3233      * Validate arguments
3234      */
3235
3236     if (!IsValidCellHandle(c_handle, &tst)) {
3237         goto fail_pts_GroupListBegin;
3238     }
3239
3240     if (iterationIdP == NULL) {
3241         tst = ADMITERATORNULL;
3242         goto fail_pts_GroupListBegin;
3243     }
3244
3245     if ((iter == NULL) || (list == NULL)) {
3246         tst = ADMNOMEM;
3247         goto fail_pts_GroupListBegin;
3248     }
3249
3250     /*
3251      * Initialize the iterator specific data
3252      */
3253
3254     list->index = 0;
3255     list->finished_retrieving = 0;
3256     list->c_handle = c_handle;
3257     list->names = NULL;
3258     list->nextstartindex = 0;
3259     list->nentries = 0;
3260     list->flag = PRGROUPS;
3261
3262     if (IteratorInit
3263         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3264          DeletePTSSpecificData, &tst)) {
3265         *iterationIdP = (void *)iter;
3266         rc = 1;
3267     }
3268
3269   fail_pts_GroupListBegin:
3270
3271     if (rc == 0) {
3272         if (iter != NULL) {
3273             free(iter);
3274         }
3275         if (list != NULL) {
3276             free(list);
3277         }
3278     }
3279
3280     if (st != NULL) {
3281         *st = tst;
3282     }
3283     return rc;
3284 }
3285
3286 /*
3287  * pts_UserListNext - get the next group in a cell.
3288  *
3289  * PARAMETERS
3290  *
3291  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3292  *
3293  * OUT groupName - upon successful completion contains the next group
3294  *
3295  * LOCKS
3296  *
3297  * The iterator mutex is held during the retrieval of the next member.
3298  *
3299  * RETURN CODES
3300  *
3301  * Returns != 0 upon successful completion.
3302  *
3303  */
3304
3305 int ADMINAPI
3306 pts_GroupListNext(const void *iterationId, char *groupName, afs_status_p st)
3307 {
3308     int rc = 0;
3309     afs_status_t tst = 0;
3310     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3311
3312     /*
3313      * Validate arguments
3314      */
3315
3316     if (iterationId == NULL) {
3317         tst = ADMITERATORNULL;
3318         goto fail_pts_GroupListNext;
3319     }
3320
3321     if (groupName == NULL) {
3322         tst = ADMPTSGROUPNAMENULL;
3323         goto fail_pts_GroupListNext;
3324     }
3325
3326     rc = IteratorNext(iter, (void *)groupName, &tst);
3327
3328   fail_pts_GroupListNext:
3329
3330     if (st != NULL) {
3331         *st = tst;
3332     }
3333     return rc;
3334 }
3335
3336 /*
3337  * pts_GroupListDone - finish using a group list iterator
3338  *
3339  * PARAMETERS
3340  *
3341  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3342  *
3343  * LOCKS
3344  *
3345  * The iterator is locked and then destroyed
3346  *
3347  * RETURN CODES
3348  *
3349  * Returns != 0 upon successful completion.
3350  *
3351  * ASSUMPTIONS
3352  *
3353  * It is the user's responsibility to make sure pts_GroupListDone
3354  * is called only once for each iterator.
3355  */
3356
3357 int ADMINAPI
3358 pts_GroupListDone(const void *iterationId, afs_status_p st)
3359 {
3360     int rc = 0;
3361     afs_status_t tst = 0;
3362     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3363
3364     /*
3365      * Validate arguments
3366      */
3367
3368     if (iterationId == NULL) {
3369         tst = ADMITERATORNULL;
3370         goto fail_pts_GroupListDone;
3371     }
3372
3373     rc = IteratorDone(iter, &tst);
3374
3375   fail_pts_GroupListDone:
3376
3377     if (st != NULL) {
3378         *st = tst;
3379     }
3380     return rc;
3381 }