ba8200bd5d7e544fe2e1736337126cb8387cc955
[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     /*
472      * Validate arguments
473      */
474
475     if (!IsValidCellHandle(c_handle, &tst)) {
476         goto fail_pts_GroupOwnerChange;
477     }
478
479     if ((newOwner == NULL) || (*newOwner == 0)) {
480         tst = ADMPTSNEWOWNERNULL;
481         goto fail_pts_GroupOwnerChange;
482     }
483
484     if ((targetGroup == NULL) || (*targetGroup == 0)) {
485         tst = ADMPTSTARGETGROUPNULL;
486         goto fail_pts_GroupOwnerChange;
487     }
488
489     if (!TranslateTwoNames
490         (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, targetGroup,
491          ADMPTSTARGETGROUPTOOLONG, &ids, &tst)) {
492         goto fail_pts_GroupOwnerChange;
493     }
494
495     /*
496      * Make the rpc
497      */
498
499     tst =
500         ubik_PR_ChangeEntry(c_handle->pts, 0, ids.idlist_val[1], "",
501                   ids.idlist_val[0], 0);
502
503     if (tst != 0) {
504         goto fail_pts_GroupOwnerChange;
505     }
506     rc = 1;
507
508   fail_pts_GroupOwnerChange:
509
510     if (ids.idlist_val != 0) {
511         free(ids.idlist_val);
512     }
513
514     if (st != NULL) {
515         *st = tst;
516     }
517     return rc;
518 }
519
520 /*
521  * pts_GroupCreate - create a new group
522  *
523  * PARAMETERS
524  *
525  * IN cellHandle - a previously opened cellHandle that corresponds
526  * to the cell where the group exists.
527  *
528  * IN newGroup - the group to be created.
529  *
530  * IN newOwner - the owner of the group.  Pass NULL if the current user
531  * is to be the new owner, or the character string of the owner otherwise.
532  *
533  * IN/OUT newGroupId - the pts id of the group.  Pass 0 to have ptserver 
534  * generate a value, != 0 to assign a value on your own.  The group id
535  * that is used to create the group is copied into this parameter in the
536  * event you pass in 0.
537  *
538  * LOCKS
539  *
540  * No locks are obtained or released by this function
541  *
542  * RETURN CODES
543  *
544  * Returns != 0 upon successful completion.
545  *
546  */
547
548 int ADMINAPI
549 pts_GroupCreate(const void *cellHandle, char *newGroup,
550                 char *newOwner, int *newGroupId, afs_status_p st)
551 {
552     int rc = 0;
553     afs_status_t tst = 0;
554     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
555     afs_int32 newOwnerId = 0;
556
557     /*
558      * Validate arguments
559      */
560
561     if (!IsValidCellHandle(c_handle, &tst)) {
562         goto fail_pts_GroupCreate;
563     }
564
565     if ((newGroup == NULL) || (*newGroup == 0)) {
566         tst = ADMPTSNEWGROUPNULL;
567         goto fail_pts_GroupCreate;
568     }
569
570     if (newGroupId == NULL) {
571         tst = ADMPTSNEWGROUPIDNULL;
572         goto fail_pts_GroupCreate;
573     }
574
575     if (*newGroupId > 0) {
576         tst = ADMPTSNEWGROUPIDPOSITIVE;
577         goto fail_pts_GroupCreate;
578     }
579
580     /*
581      * If a newOwner was specified, validate that it exists
582      */
583
584     if (newOwner != NULL) {
585         if (!TranslateOneName
586             (c_handle, newOwner, ADMPTSNEWOWNERTOOLONG, &newOwnerId, &tst)) {
587             goto fail_pts_GroupCreate;
588         }
589     }
590
591     /*
592      * We make a different rpc based upon the input to this function
593      */
594
595     if (*newGroupId != 0) {
596         tst =
597             ubik_PR_INewEntry(c_handle->pts, 0, newGroup, *newGroupId,
598                       newOwnerId);
599     } else {
600         tst =
601             ubik_PR_NewEntry(c_handle->pts, 0, newGroup, PRGRP,
602                       newOwnerId, newGroupId);
603     }
604
605     if (tst != 0) {
606         goto fail_pts_GroupCreate;
607     }
608     rc = 1;
609
610   fail_pts_GroupCreate:
611
612     if (st != NULL) {
613         *st = tst;
614     }
615     return rc;
616 }
617
618 /*
619  * GetGroupAccess - a small convenience function for setting
620  * permissions.
621  *
622  * PARAMETERS
623  *
624  * IN access - a pointer to a pts_groupAccess_t to be set with the
625  * correct permission.
626  *
627  * IN flag - the current permission flag used to derive the permission.
628  *
629  * LOCKS
630  *
631  * No locks are obtained or released by this function
632  *
633  * RETURN CODES
634  *
635  * Since this function cannot fail, it returns void.
636  *
637  */
638
639 static void
640 GetGroupAccess(pts_groupAccess_p access, afs_int32 flag)
641 {
642
643     *access = PTS_GROUP_OWNER_ACCESS;
644     if (flag == 0) {
645         *access = PTS_GROUP_OWNER_ACCESS;
646     } else if (flag == 1) {
647         *access = PTS_GROUP_ACCESS;
648     } else if (flag == 2) {
649         *access = PTS_GROUP_ANYUSER_ACCESS;
650     }
651 }
652
653
654 /*
655  * pts_GroupGet - retrieve information about a particular group.
656  *
657  * PARAMETERS
658  *
659  * IN cellHandle - a previously opened cellHandle that corresponds
660  * to the cell where the group exists.
661  *
662  * IN groupName - the group to retrieve.
663  *
664  * OUT groupP - a pointer to a pts_GroupEntry_t structure that upon
665  * successful completion is filled with information about groupName.
666  *
667  * LOCKS
668  *
669  * No locks are obtained or released by this function
670  *
671  * RETURN CODES
672  *
673  * Returns != 0 upon successful completion.
674  *
675  */
676
677 int ADMINAPI
678 pts_GroupGet(const void *cellHandle, const char *groupName,
679              pts_GroupEntry_p groupP, afs_status_p st)
680 {
681     int rc = 0;
682     afs_status_t tst = 0;
683     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
684     afs_int32 groupId = 0;
685     afs_int32 flags;
686     afs_int32 twobit;
687     struct prcheckentry groupEntry;
688     idlist ids;
689     afs_int32 ptsids[2];
690     namelist names;
691
692     /*
693      * Validate arguments
694      */
695
696     if (!IsValidCellHandle(c_handle, &tst)) {
697         goto fail_pts_GroupGet;
698     }
699
700     if ((groupName == NULL) || (*groupName == 0)) {
701         tst = ADMPTSGROUPNAMENULL;
702         goto fail_pts_GroupGet;
703     }
704
705     if (groupP == NULL) {
706         tst = ADMPTSGROUPPNULL;
707         goto fail_pts_GroupGet;
708     }
709
710     /*
711      * Translate the group name into an id.
712      */
713
714     if (!TranslateOneName
715         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
716         goto fail_pts_GroupGet;
717     }
718
719     /*
720      * Retrieve information about the group
721      */
722
723     tst = ubik_PR_ListEntry(c_handle->pts, 0, groupId, &groupEntry);
724
725     if (tst != 0) {
726         goto fail_pts_GroupGet;
727     }
728
729     groupP->membershipCount = groupEntry.count;
730     groupP->nameUid = groupEntry.id;
731     groupP->ownerUid = groupEntry.owner;
732     groupP->creatorUid = groupEntry.creator;
733     strncpy(groupP->name, groupEntry.name, PTS_MAX_NAME_LEN);
734     groupP->name[PTS_MAX_NAME_LEN - 1] = '\0';
735     /*
736      * Set the access rights based upon the value of the flags member
737      * of the groupEntry struct.
738      *
739      * To the best of my ability to decypher the pts code, it looks like
740      * the rights are stored in flags as follows:
741      *
742      * I number my bits from least significant to most significant starting
743      * with 0.
744      *
745      * remove - bit 0
746      *     if bit 0 == 0 -> r access is denied
747      *     if bit 0 == 1 -> r access is granted
748      *
749      * add - bits 1 and 2
750      *     if bit 2 == 0 and bit 1 == 0 -> a access is denied
751      *     if bit 2 == 0 and bit 1 == 1 -> a access is granted
752      *     if bit 2 == 1 and bit 1 == 0 -> A access is granted
753      *     if bit 2 == 1 and bit 1 == 1 -> this is an error
754      *
755      * membership - bits 3 and 4
756      *     if bit 4 == 0 and bit 3 == 0 -> m access is denied
757      *     if bit 4 == 0 and bit 3 == 1 -> m access is granted
758      *     if bit 4 == 1 and bit 3 == 0 -> M access is granted
759      *     if bit 4 == 1 and bit 3 == 1 -> this is an error
760      *
761      * owned - bit 5
762      *     if bit 5 == 0 -> O access is denied
763      *     if bit 5 == 1 -> O access is granted
764      *
765      * status - bits 6 and 7
766      *     if bit 7 == 0 and bit 6 == 0 -> s access is denied
767      *     if bit 7 == 0 and bit 6 == 1 -> s access is granted
768      *     if bit 7 == 1 and bit 6 == 0 -> S access is granted
769      *     if bit 7 == 1 and bit 6 == 1 -> this is an error
770      *
771      * For cases where the permission doesn't make sense for the
772      * type of entry, or where an error occurs, we ignore it.
773      * This is the behavior of the pts code.
774      */
775
776     flags = groupEntry.flags;
777     if (flags & 1) {
778         groupP->listDelete = PTS_GROUP_ACCESS;
779     } else {
780         groupP->listDelete = PTS_GROUP_OWNER_ACCESS;
781     }
782
783     flags = flags >> 1;
784     twobit = flags & 3;
785
786     GetGroupAccess(&groupP->listAdd, twobit);
787
788     flags = flags >> 2;
789     twobit = flags & 3;
790
791     GetGroupAccess(&groupP->listMembership, twobit);
792
793     flags = flags >> 2;
794
795     if (flags & 1) {
796         groupP->listGroupsOwned = PTS_GROUP_ANYUSER_ACCESS;
797     } else {
798         groupP->listGroupsOwned = PTS_GROUP_OWNER_ACCESS;
799     }
800
801     flags = flags >> 1;
802     twobit = flags & 3;
803
804     GetGroupAccess(&groupP->listStatus, twobit);
805
806     /* 
807      * Make another rpc and translate the owner and creator ids into
808      * character strings.
809      */
810
811     ids.idlist_len = 2;
812     ids.idlist_val = ptsids;
813     ptsids[0] = groupEntry.owner;
814     ptsids[1] = groupEntry.creator;
815     names.namelist_len = 0;
816     names.namelist_val = 0;
817
818
819     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
820         goto fail_pts_GroupGet;
821     }
822
823     strncpy(groupP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
824     groupP->owner[PTS_MAX_NAME_LEN - 1] = '\0';
825     strncpy(groupP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
826     groupP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
827     free(names.namelist_val);
828     rc = 1;
829
830   fail_pts_GroupGet:
831
832     if (st != NULL) {
833         *st = tst;
834     }
835     return rc;
836 }
837
838 /*
839  * EntryDelete - delete a pts entry (group or user).
840  *
841  * PARAMETERS
842  *
843  * IN cellHandle - a previously opened cellHandle that corresponds
844  * to the cell where the group exists.
845  *
846  * IN entryName - the entry to be deleted.
847  *
848  * IN error1 - the error status to be returned in the event that entryName is
849  * null.
850  *
851  * IN error2 - the error status to be returned in the event that entryName is
852  * too long.
853  *
854  * LOCKS
855  *
856  * No locks are obtained or released by this function
857  *
858  * RETURN CODES
859  *
860  * Returns != 0 upon successful completion.
861  *
862  */
863
864 static int
865 EntryDelete(const void *cellHandle, const char *entryName,
866             afs_status_t error1, afs_status_t error2, afs_status_p st)
867 {
868     int rc = 0;
869     afs_status_t tst = 0;
870     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
871     afs_int32 entryId = 0;
872
873     /*
874      * Validate arguments
875      */
876
877     if (!IsValidCellHandle(c_handle, &tst)) {
878         goto fail_EntryDelete;
879     }
880
881     if ((entryName == NULL) || (*entryName == 0)) {
882         tst = error1;
883         goto fail_EntryDelete;
884     }
885
886     /*
887      * Translate the entry name into an id.
888      */
889
890     if (!TranslateOneName(c_handle, entryName, error2, &entryId, &tst)) {
891         goto fail_EntryDelete;
892     }
893
894     /*
895      * Make the rpc
896      */
897
898     tst = ubik_PR_Delete(c_handle->pts, 0, entryId);
899
900     if (tst != 0) {
901         goto fail_EntryDelete;
902     }
903     rc = 1;
904
905   fail_EntryDelete:
906
907     if (st != NULL) {
908         *st = tst;
909     }
910     return rc;
911 }
912
913
914 /*
915  * pts_GroupDelete - delete a group
916  *
917  * PARAMETERS
918  *
919  * IN cellHandle - a previously opened cellHandle that corresponds
920  * to the cell where the group exists.
921  *
922  * IN groupName - the group to be deleted.
923  *
924  * LOCKS
925  *
926  * No locks are obtained or released by this function
927  *
928  * RETURN CODES
929  *
930  * Returns != 0 upon successful completion.
931  *
932  */
933
934 int ADMINAPI
935 pts_GroupDelete(const void *cellHandle, const char *groupName,
936                 afs_status_p st)
937 {
938
939     return EntryDelete(cellHandle, groupName, ADMPTSGROUPNAMENULL,
940                        ADMPTSGROUPNAMETOOLONG, st);
941 }
942
943 /*
944  * pts_GroupMaxGet - get the maximum in use group id.
945  *
946  * PARAMETERS
947  *
948  * IN cellHandle - a previously opened cellHandle that corresponds
949  * to the cell where the group exists.
950  *
951  * OUT maxGroupId - upon successful completion contains the maximum
952  * group Id in use at the server.
953  *
954  * LOCKS
955  *
956  * No locks are obtained or released by this function
957  *
958  * RETURN CODES
959  *
960  * Returns != 0 upon successful completion.
961  *
962  */
963
964 int ADMINAPI
965 pts_GroupMaxGet(const void *cellHandle, int *maxGroupId, afs_status_p st)
966 {
967     int rc = 0;
968     afs_status_t tst = 0;
969     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
970     afs_int32 maxUserId = 0;
971
972     /*
973      * Validate arguments
974      */
975
976     if (!IsValidCellHandle(c_handle, &tst)) {
977         goto fail_pts_GroupMaxGet;
978     }
979
980     if (maxGroupId == NULL) {
981         tst = ADMPTSMAXGROUPIDNULL;
982         goto fail_pts_GroupMaxGet;
983     }
984
985     tst = ubik_PR_ListMax(c_handle->pts, 0, &maxUserId, maxGroupId);
986
987     if (tst != 0) {
988         goto fail_pts_GroupMaxGet;
989     }
990     rc = 1;
991
992   fail_pts_GroupMaxGet:
993
994     if (st != NULL) {
995         *st = tst;
996     }
997     return rc;
998 }
999
1000 /*
1001  * pts_GroupMaxSet - set the maximum in use group id.
1002  *
1003  * PARAMETERS
1004  *
1005  * IN cellHandle - a previously opened cellHandle that corresponds
1006  * to the cell where the group exists.
1007  *
1008  * IN maxGroupId - the new maximum group id.
1009  *
1010  * LOCKS
1011  *
1012  * No locks are obtained or released by this function
1013  *
1014  * RETURN CODES
1015  *
1016  * Returns != 0 upon successful completion.
1017  *
1018  */
1019
1020 int ADMINAPI
1021 pts_GroupMaxSet(const void *cellHandle, int maxGroupId, afs_status_p st)
1022 {
1023     int rc = 0;
1024     afs_status_t tst = 0;
1025     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1026
1027     /*
1028      * Validate arguments
1029      */
1030
1031     if (!IsValidCellHandle(c_handle, &tst)) {
1032         goto fail_pts_GroupMaxSet;
1033     }
1034
1035     tst = ubik_PR_SetMax(c_handle->pts, 0, maxGroupId, PRGRP);
1036
1037     if (tst != 0) {
1038         goto fail_pts_GroupMaxSet;
1039     }
1040     rc = 1;
1041
1042   fail_pts_GroupMaxSet:
1043
1044     if (st != NULL) {
1045         *st = tst;
1046     }
1047     return rc;
1048 }
1049
1050 /*
1051  * NOTE
1052  *
1053  * I'm not using the common iterator pattern here since the retrival
1054  * of the member list is actually accomplished in 1 rpc.  There's no
1055  * sense in trying to fit this pts specific behavior into the more
1056  * generic model, so instead the Begin functions actually do all the
1057  * rpc work and the next/done functions just manipulate the retrieved
1058  * data.
1059  */
1060
1061 typedef struct pts_group_member_list_iterator {
1062     int begin_magic;
1063     int is_valid;
1064     pthread_mutex_t mutex;      /* hold to manipulate this structure */
1065     prlist ids;
1066     namelist names;
1067     int index;
1068     int end_magic;
1069 } pts_group_member_list_iterator_t, *pts_group_member_list_iterator_p;
1070
1071 /*
1072  * pts_GroupMemberListBegin - begin iterating over the list of members
1073  * of a particular group.
1074  *
1075  * PARAMETERS
1076  *
1077  * IN iter - an iterator previously returned by pts_GroupMemberListBegin
1078  *
1079  * LOCKS
1080  *
1081  * No locks are obtained or released by this function
1082  *
1083  * RETURN CODES
1084  *
1085  * Returns != 0 upon successful completion.
1086  *
1087  */
1088
1089 static int
1090 IsValidPtsGroupMemberListIterator(pts_group_member_list_iterator_p iter,
1091                                   afs_status_p st)
1092 {
1093     int rc = 0;
1094     afs_status_t tst = 0;
1095
1096     if (iter == NULL) {
1097         tst = ADMITERATORNULL;
1098         goto fail_IsValidPtsGroupMemberListIterator;
1099     }
1100
1101     if ((iter->begin_magic != BEGIN_MAGIC) || (iter->end_magic != END_MAGIC)) {
1102         tst = ADMITERATORBADMAGICNULL;
1103         goto fail_IsValidPtsGroupMemberListIterator;
1104     }
1105
1106     if (iter->is_valid == 0) {
1107         tst = ADMITERATORINVALID;
1108         goto fail_IsValidPtsGroupMemberListIterator;
1109     }
1110     rc = 1;
1111
1112   fail_IsValidPtsGroupMemberListIterator:
1113
1114     if (st != NULL) {
1115         *st = tst;
1116     }
1117     return rc;
1118 }
1119
1120 /*
1121  * MemberListBegin - an internal function which is used to get both
1122  * the list of members in a group and the list of groups a user belongs
1123  * to.
1124  *
1125  * PARAMETERS
1126  *
1127  * IN cellHandle - a previously opened cellHandle that corresponds
1128  * to the cell where the group exists.
1129  *
1130  * IN name - the name whose membership will be retrieved.
1131  *
1132  * OUT iterationIdP - upon successful completion contains a iterator that
1133  * can be passed to pts_GroupMemberListNext or pts_UserMemberListNext
1134  *
1135  * LOCKS
1136  *
1137  * No locks are obtained or released by this function
1138  *
1139  * RETURN CODES
1140  *
1141  * Returns != 0 upon successful completion.
1142  *
1143  */
1144
1145 static int
1146 MemberListBegin(const void *cellHandle, const char *name, afs_status_t error1,
1147                 afs_status_t error2, void **iterationIdP, afs_status_p st)
1148 {
1149     int rc = 0;
1150     afs_status_t tst = 0;
1151     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1152     afs_int32 groupId = 0;
1153     afs_int32 exceeded = 0;
1154     pts_group_member_list_iterator_p iter = (pts_group_member_list_iterator_p)
1155         malloc(sizeof(pts_group_member_list_iterator_t));
1156     int iter_allocated = 0;
1157     int ids_allocated = 0;
1158     int names_allocated = 0;
1159     int mutex_inited = 0;
1160
1161     /*
1162      * Validate arguments
1163      */
1164
1165     if (!IsValidCellHandle(c_handle, &tst)) {
1166         goto fail_MemberListBegin;
1167     }
1168
1169     if ((name == NULL) || (*name == 0)) {
1170         tst = ADMPTSGROUPNAMENULL;
1171         goto fail_MemberListBegin;
1172     }
1173
1174     if (iterationIdP == NULL) {
1175         tst = ADMITERATORNULL;
1176         goto fail_MemberListBegin;
1177     }
1178
1179     if (iter == NULL) {
1180         tst = ADMNOMEM;
1181         goto fail_MemberListBegin;
1182     }
1183
1184     iter_allocated = 1;
1185
1186     /*
1187      * Translate the name into an id.
1188      */
1189
1190     if (!TranslateOneName
1191         (c_handle, name, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1192         goto fail_MemberListBegin;
1193     }
1194
1195     if (pthread_mutex_init(&iter->mutex, 0)) {
1196         tst = ADMMUTEXINIT;
1197         goto fail_MemberListBegin;
1198     }
1199
1200     mutex_inited = 1;
1201
1202     iter->ids.prlist_len = 0;
1203     iter->ids.prlist_val = 0;
1204
1205     tst =
1206         ubik_PR_ListElements(c_handle->pts, 0, groupId, &iter->ids,
1207                   &exceeded);
1208
1209     if (tst != 0) {
1210         goto fail_MemberListBegin;
1211     }
1212
1213     if (exceeded != 0) {
1214         tst = ADMPTSGROUPMEMEXCEEDED;
1215         goto fail_MemberListBegin;
1216     }
1217
1218     ids_allocated = 1;
1219     iter->names.namelist_len = 0;
1220     iter->names.namelist_val = 0;
1221
1222     if (!TranslatePTSIds
1223         (c_handle, &iter->names, (idlist *) & iter->ids, &tst)) {
1224         goto fail_MemberListBegin;
1225     }
1226
1227     names_allocated = 1;
1228     iter->begin_magic = BEGIN_MAGIC;
1229     iter->end_magic = END_MAGIC;
1230     iter->index = 0;
1231     iter->is_valid = 1;
1232
1233     *iterationIdP = (void *)iter;
1234     rc = 1;
1235
1236   fail_MemberListBegin:
1237
1238     if (ids_allocated) {
1239         free(iter->ids.prlist_val);
1240     }
1241
1242     if (rc == 0) {
1243         if (names_allocated) {
1244             free(iter->names.namelist_val);
1245         }
1246         if (mutex_inited) {
1247             pthread_mutex_destroy(&iter->mutex);
1248         }
1249         if (iter_allocated) {
1250             free(iter);
1251         }
1252     }
1253     if (st != NULL) {
1254         *st = tst;
1255     }
1256     return rc;
1257 }
1258
1259
1260 /*
1261  * pts_GroupMemberListBegin - begin iterating over the list of members
1262  * of a particular group.
1263  *
1264  * PARAMETERS
1265  *
1266  * IN cellHandle - a previously opened cellHandle that corresponds
1267  * to the cell where the group exists.
1268  *
1269  * IN groupName - the group whose members will be returned.
1270  *
1271  * OUT iterationIdP - upon successful completion contains a iterator that
1272  * can be passed to pts_GroupMemberListNext.
1273  *
1274  * LOCKS
1275  *
1276  * No locks are obtained or released by this function
1277  *
1278  * RETURN CODES
1279  *
1280  * Returns != 0 upon successful completion.
1281  *
1282  */
1283
1284 int ADMINAPI
1285 pts_GroupMemberListBegin(const void *cellHandle, const char *groupName,
1286                          void **iterationIdP, afs_status_p st)
1287 {
1288     return MemberListBegin(cellHandle, groupName, ADMPTSGROUPNAMENULL,
1289                            ADMPTSGROUPNAMETOOLONG, iterationIdP, st);
1290 }
1291
1292 /*
1293  * pts_GroupMemberListNext - get the next member of a group
1294  *
1295  * PARAMETERS
1296  *
1297  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1298  *
1299  * OUT memberName - upon successful completion contains the next member of
1300  * a group.
1301  *
1302  * LOCKS
1303  *
1304  * The iterator mutex is held during the retrieval of the next member.
1305  *
1306  * RETURN CODES
1307  *
1308  * Returns != 0 upon successful completion.
1309  *
1310  */
1311
1312 int ADMINAPI
1313 pts_GroupMemberListNext(const void *iterationId, char *memberName,
1314                         afs_status_p st)
1315 {
1316     int rc = 0;
1317     afs_status_t tst = 0;
1318     pts_group_member_list_iterator_p iter =
1319         (pts_group_member_list_iterator_p) iterationId;
1320     int mutex_locked = 0;
1321
1322     /*
1323      * Validate arguments
1324      */
1325
1326     if (iter == NULL) {
1327         tst = ADMITERATORNULL;
1328         goto fail_pts_GroupMemberListNext;
1329     }
1330
1331     if (memberName == NULL) {
1332         tst = ADMPTSMEMBERNAMENULL;
1333         goto fail_pts_GroupMemberListNext;
1334     }
1335
1336     /*
1337      * Lock the mutex and check the validity of the iterator
1338      */
1339
1340     if (pthread_mutex_lock(&iter->mutex)) {
1341         tst = ADMMUTEXLOCK;
1342         goto fail_pts_GroupMemberListNext;
1343     }
1344
1345     mutex_locked = 1;
1346
1347     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1348         goto fail_pts_GroupMemberListNext;
1349     }
1350
1351     /*
1352      * Check to see if we've copied out all the data.  If we haven't,
1353      * copy another item.  If we have, mark the iterator done.
1354      */
1355
1356     if (iter->index >= iter->names.namelist_len) {
1357         tst = ADMITERATORDONE;
1358         goto fail_pts_GroupMemberListNext;
1359     } else {
1360         strcpy(memberName, iter->names.namelist_val[iter->index]);
1361         iter->index++;
1362     }
1363     rc = 1;
1364
1365   fail_pts_GroupMemberListNext:
1366
1367     if (mutex_locked) {
1368         pthread_mutex_unlock(&iter->mutex);
1369     }
1370
1371     if (st != NULL) {
1372         *st = tst;
1373     }
1374     return rc;
1375 }
1376
1377 /*
1378  * pts_GroupMemberListDone - finish using a member list iterator
1379  *
1380  * PARAMETERS
1381  *
1382  * IN iterationId - an iterator previously returned by pts_GroupMemberListBegin
1383  *
1384  * LOCKS
1385  *
1386  * The iterator is locked and then destroyed
1387  *
1388  * RETURN CODES
1389  *
1390  * Returns != 0 upon successful completion.
1391  *
1392  * ASSUMPTIONS
1393  *
1394  * It is the user's responsibility to make sure pts_GroupMemberListDone
1395  * is called only once for each iterator.
1396  */
1397
1398 int ADMINAPI
1399 pts_GroupMemberListDone(const void *iterationId, afs_status_p st)
1400 {
1401     int rc = 0;
1402     afs_status_t tst = 0;
1403     pts_group_member_list_iterator_p iter =
1404         (pts_group_member_list_iterator_p) iterationId;
1405     int mutex_locked = 0;
1406
1407     /*
1408      * Validate arguments
1409      */
1410
1411     if (iter == NULL) {
1412         tst = ADMITERATORNULL;
1413         goto fail_pts_GroupMemberListDone;
1414     }
1415
1416     /*
1417      * Lock the mutex and check the validity of the iterator
1418      */
1419
1420     if (pthread_mutex_lock(&iter->mutex)) {
1421         tst = ADMMUTEXLOCK;
1422         goto fail_pts_GroupMemberListDone;
1423     }
1424
1425     mutex_locked = 1;
1426
1427     if (!IsValidPtsGroupMemberListIterator(iter, &tst)) {
1428         goto fail_pts_GroupMemberListDone;
1429     }
1430
1431     /*
1432      * Free the namelist and the iterator.
1433      */
1434
1435     pthread_mutex_destroy(&iter->mutex);
1436     mutex_locked = 0;
1437     iter->is_valid = 0;
1438     free(iter->names.namelist_val);
1439     free(iter);
1440     rc = 1;
1441
1442   fail_pts_GroupMemberListDone:
1443
1444     if (mutex_locked) {
1445         pthread_mutex_unlock(&iter->mutex);
1446     }
1447
1448     if (st != NULL) {
1449         *st = tst;
1450     }
1451     return rc;
1452 }
1453
1454 /*
1455  * pts_GroupMemberRemove - remove a member from a group.
1456  *
1457  * PARAMETERS
1458  *
1459  * IN cellHandle - a previously opened cellHandle that corresponds
1460  * to the cell where the group exists.
1461  *
1462  * IN userName - the user to remove.
1463  *
1464  * IN groupName - the group to modify
1465  *
1466  * LOCKS
1467  *
1468  * No locks are held by this function
1469  *
1470  * RETURN CODES
1471  *
1472  * Returns != 0 upon successful completion.
1473  *
1474  */
1475
1476 int ADMINAPI
1477 pts_GroupMemberRemove(const void *cellHandle, const char *userName,
1478                       const char *groupName, afs_status_p st)
1479 {
1480     int rc = 0;
1481     afs_status_t tst = 0;
1482     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1483     idlist ids;
1484
1485     ids.idlist_val = NULL;
1486
1487     /*
1488      * Validate arguments
1489      */
1490
1491     if (!IsValidCellHandle(c_handle, &tst)) {
1492         goto fail_pts_GroupMemberRemove;
1493     }
1494
1495     if ((userName == NULL) || (*userName == 0)) {
1496         tst = ADMPTSUSERNAMENULL;
1497         goto fail_pts_GroupMemberRemove;
1498     }
1499
1500     if ((groupName == NULL) || (*groupName == 0)) {
1501         tst = ADMPTSGROUPNAMENULL;
1502         goto fail_pts_GroupMemberRemove;
1503     }
1504
1505     if (!TranslateTwoNames
1506         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
1507          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
1508         goto fail_pts_GroupMemberRemove;
1509     }
1510
1511     /*
1512      * Make the rpc
1513      */
1514
1515     tst =
1516         ubik_PR_RemoveFromGroup(c_handle->pts, 0, ids.idlist_val[0],
1517                   ids.idlist_val[1]);
1518
1519     if (tst != 0) {
1520         goto fail_pts_GroupMemberRemove;
1521     }
1522     rc = 1;
1523
1524   fail_pts_GroupMemberRemove:
1525
1526     if (ids.idlist_val != 0) {
1527         free(ids.idlist_val);
1528     }
1529
1530     if (st != NULL) {
1531         *st = tst;
1532     }
1533     return rc;
1534 }
1535
1536 /*
1537  * pts_GroupRename - change the name of a group
1538  *
1539  * PARAMETERS
1540  *
1541  * IN cellHandle - a previously opened cellHandle that corresponds
1542  * to the cell where the group exists.
1543  *
1544  * IN oldName - the current group name
1545  *
1546  * IN newName - the new group name
1547  *
1548  * LOCKS
1549  *
1550  * No locks are held by this function
1551  *
1552  * RETURN CODES
1553  *
1554  * Returns != 0 upon successful completion.
1555  *
1556  */
1557
1558 int ADMINAPI
1559 pts_GroupRename(const void *cellHandle, const char *oldName,
1560                 char *newName, afs_status_p st)
1561 {
1562     int rc = 0;
1563     afs_status_t tst = 0;
1564     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1565     afs_int32 groupId = 0;
1566
1567     /*
1568      * Validate arguments
1569      */
1570
1571     if (!IsValidCellHandle(c_handle, &tst)) {
1572         goto fail_pts_GroupRename;
1573     }
1574
1575     if ((newName == NULL) || (*newName == 0)) {
1576         tst = ADMPTSNEWNAMENULL;
1577         goto fail_pts_GroupRename;
1578     }
1579
1580     if ((oldName == NULL) || (*oldName == 0)) {
1581         tst = ADMPTSOLDNAMENULL;
1582         goto fail_pts_GroupRename;
1583     }
1584
1585     /*
1586      * Translate the group name into an id.
1587      */
1588
1589     if (!TranslateOneName
1590         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &groupId, &tst)) {
1591         goto fail_pts_GroupRename;
1592     }
1593
1594     /*
1595      * Make the rpc
1596      */
1597
1598     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, groupId, newName, 0, 0);
1599
1600     if (tst != 0) {
1601         goto fail_pts_GroupRename;
1602     }
1603     rc = 1;
1604
1605   fail_pts_GroupRename:
1606
1607     if (st != NULL) {
1608         *st = tst;
1609     }
1610     return rc;
1611 }
1612
1613 /*
1614  * SetGroupAccess - translate our Access notation to pts flags.
1615  *
1616  * PARAMETERS
1617  *
1618  * IN rights - the permissions.
1619  *
1620  * OUT flags - a pointer to an afs_int32 structure that 
1621  * contains the flags to pass to pts.
1622  *
1623  * LOCKS
1624  *
1625  * No locks are held by this function
1626  *
1627  * RETURN CODES
1628  *
1629  * Returns != 0 upon successful completion.
1630  *
1631  */
1632
1633 static int
1634 SetGroupAccess(const pts_GroupUpdateEntry_p rights, afs_int32 * flags,
1635                afs_status_p st)
1636 {
1637     int rc = 0;
1638     afs_status_t tst = 0;
1639
1640     *flags = 0;
1641
1642     if (rights->listDelete == PTS_GROUP_ACCESS) {
1643         *flags |= 1;
1644     } else if (rights->listDelete == PTS_GROUP_ANYUSER_ACCESS) {
1645         tst = ADMPTSINVALIDGROUPDELETEPERM;
1646         goto fail_SetGroupAccess;
1647     }
1648
1649     if (rights->listAdd == PTS_GROUP_ACCESS) {
1650         *flags |= 2;
1651     } else if (rights->listAdd == PTS_GROUP_ANYUSER_ACCESS) {
1652         *flags |= 4;
1653     }
1654
1655     if (rights->listMembership == PTS_GROUP_ACCESS) {
1656         *flags |= 8;
1657     } else if (rights->listMembership == PTS_GROUP_ANYUSER_ACCESS) {
1658         *flags |= 16;
1659     }
1660
1661     if (rights->listGroupsOwned == PTS_GROUP_ANYUSER_ACCESS) {
1662         *flags |= 32;
1663     } else if (rights->listGroupsOwned == PTS_GROUP_ACCESS) {
1664         tst = ADMPTSINVALIDGROUPSOWNEDPERM;
1665         goto fail_SetGroupAccess;
1666     }
1667
1668     if (rights->listStatus == PTS_GROUP_ACCESS) {
1669         *flags |= 64;
1670     } else if (rights->listStatus == PTS_GROUP_ANYUSER_ACCESS) {
1671         *flags |= 128;
1672     }
1673     rc = 1;
1674
1675   fail_SetGroupAccess:
1676
1677     if (st != NULL) {
1678         *st = tst;
1679     }
1680     return rc;
1681 }
1682
1683 /*
1684  * pts_GroupModify - change the contents of a group entry.
1685  *
1686  * PARAMETERS
1687  *
1688  * IN cellHandle - a previously opened cellHandle that corresponds
1689  * to the cell where the group exists.
1690  *
1691  * IN groupName - the group to change
1692  *
1693  * OUT newEntryP - a pointer to a pts_GroupUpdateEntry_t structure that 
1694  * contains the new information for the group.
1695  *
1696  * LOCKS
1697  *
1698  * No locks are held by this function
1699  *
1700  * RETURN CODES
1701  *
1702  * Returns != 0 upon successful completion.
1703  *
1704  */
1705
1706 int ADMINAPI
1707 pts_GroupModify(const void *cellHandle, const char *groupName,
1708                 const pts_GroupUpdateEntry_p newEntryP, afs_status_p st)
1709 {
1710     int rc = 0;
1711     afs_status_t tst = 0;
1712     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1713     afs_int32 groupId = 0;
1714     afs_int32 flags = 0;
1715
1716     /*
1717      * Validate arguments
1718      */
1719
1720     if (!IsValidCellHandle(c_handle, &tst)) {
1721         goto fail_pts_GroupModify;
1722     }
1723
1724     if ((groupName == NULL) || (*groupName == 0)) {
1725         tst = ADMPTSGROUPNAMENULL;
1726         goto fail_pts_GroupModify;
1727     }
1728
1729
1730     if (newEntryP == NULL) {
1731         tst = ADMPTSNEWENTRYPNULL;
1732         goto fail_pts_GroupModify;
1733     }
1734
1735     /*
1736      * Translate the group name into an id.
1737      */
1738
1739     if (!TranslateOneName
1740         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1741         goto fail_pts_GroupModify;
1742     }
1743
1744     /*
1745      * Set the flags argument
1746      */
1747
1748     if (!SetGroupAccess(newEntryP, &flags, &tst)) {
1749         goto fail_pts_GroupModify;
1750     }
1751
1752     /*
1753      * Make the rpc
1754      */
1755
1756     tst =
1757         ubik_PR_SetFieldsEntry(c_handle->pts, 0, groupId, PR_SF_ALLBITS,
1758                   flags, 0, 0, 0, 0);
1759
1760     if (tst != 0) {
1761         goto fail_pts_GroupModify;
1762     }
1763     rc = 1;
1764
1765   fail_pts_GroupModify:
1766
1767     if (st != NULL) {
1768         *st = tst;
1769     }
1770     return rc;
1771 }
1772
1773 /*
1774  * pts_UserCreate - create a new user.
1775  *
1776  * PARAMETERS
1777  *
1778  * IN cellHandle - a previously opened cellHandle that corresponds
1779  * to the cell where the group exists.
1780  *
1781  * IN newUser - the name of the new user.
1782  *
1783  * IN newUserId - the id to assign to the new user.  Pass 0 to have the
1784  * id assigned by pts.
1785  *
1786  * LOCKS
1787  *
1788  * No locks are held by this function
1789  *
1790  * RETURN CODES
1791  *
1792  * Returns != 0 upon successful completion.
1793  *
1794  */
1795
1796 int ADMINAPI
1797 pts_UserCreate(const void *cellHandle, char *userName, int *newUserId,
1798                afs_status_p st)
1799 {
1800     int rc = 0;
1801     afs_status_t tst = 0;
1802     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1803
1804     /*
1805      * Validate arguments
1806      */
1807
1808     if (!IsValidCellHandle(c_handle, &tst)) {
1809         goto fail_pts_UserCreate;
1810     }
1811
1812     if ((userName == NULL) || (*userName == 0)) {
1813         tst = ADMPTSUSERNAMENULL;
1814         goto fail_pts_UserCreate;
1815     }
1816
1817     if (newUserId == NULL) {
1818         tst = ADMPTSNEWUSERIDNULL;
1819         goto fail_pts_UserCreate;
1820     }
1821
1822     /*
1823      * We make a different rpc based upon the input to this function
1824      */
1825
1826     if (*newUserId != 0) {
1827         tst =
1828             ubik_PR_INewEntry(c_handle->pts, 0, userName, *newUserId,
1829                       0);
1830     } else {
1831         tst =
1832             ubik_PR_NewEntry(c_handle->pts, 0, userName, 0, 0,
1833                       newUserId);
1834     }
1835
1836     if (tst != 0) {
1837         goto fail_pts_UserCreate;
1838     }
1839     rc = 1;
1840
1841   fail_pts_UserCreate:
1842
1843     if (st != NULL) {
1844         *st = tst;
1845     }
1846     return rc;
1847 }
1848
1849 /*
1850  * pts_UserDelete - delete a user.
1851  *
1852  * PARAMETERS
1853  *
1854  * IN cellHandle - a previously opened cellHandle that corresponds
1855  * to the cell where the group exists.
1856  *
1857  * IN user - the name of the user to delete.
1858  *
1859  * LOCKS
1860  *
1861  * No locks are held by this function
1862  *
1863  * RETURN CODES
1864  *
1865  * Returns != 0 upon successful completion.
1866  *
1867  */
1868
1869 int ADMINAPI
1870 pts_UserDelete(const void *cellHandle, const char *userName, afs_status_p st)
1871 {
1872     return EntryDelete(cellHandle, userName, ADMPTSUSERNAMENULL,
1873                        ADMPTSUSERNAMETOOLONG, st);
1874 }
1875
1876
1877 /*
1878  * GetUserAccess - a small convenience function for setting
1879  * permissions.
1880  *
1881  * PARAMETERS
1882  *
1883  * IN access - a pointer to a pts_userAccess_t to be set with the
1884  * correct permission.
1885  *
1886  * IN flag - the current permission flag used to derive the permission.
1887  *
1888  * LOCKS
1889  *
1890  * No locks are obtained or released by this function
1891  *
1892  * RETURN CODES
1893  *
1894  * Since this function cannot fail, it returns void.
1895  *
1896  */
1897
1898 static void
1899 GetUserAccess(pts_userAccess_p access, afs_int32 flag)
1900 {
1901
1902     *access = PTS_USER_OWNER_ACCESS;
1903     if (flag == 2) {
1904         *access = PTS_USER_ANYUSER_ACCESS;
1905     }
1906 }
1907
1908 /*
1909  * IsAdministrator - determine if a user is an administrator.
1910  *
1911  * PARAMETERS
1912  *
1913  * IN cellHandle - a previously opened cellHandle that corresponds
1914  * to the cell where the group exists.
1915  *
1916  * IN userEntry - the user data for the user in question.
1917  *
1918  * OUT admin - set to 1 if the user is an administrator, 0 otherwise.
1919  *
1920  * LOCKS
1921  *
1922  * No locks are held by this function
1923  *
1924  * RETURN CODES
1925  *
1926  * Returns != 0 upon successful completion.
1927  *
1928  */
1929
1930 static int
1931 IsAdministrator(const afs_cell_handle_p c_handle, afs_int32 userId,
1932                 int *admin, afs_status_p st)
1933 {
1934     int rc = 0;
1935     afs_status_t tst = 0;
1936     afs_int32 adminId = 0;
1937     afs_int32 isAdmin = 0;
1938
1939     *admin = 0;
1940
1941     if (userId == SYSADMINID) {
1942         *admin = 1;
1943     } else {
1944         if (!TranslateOneName
1945             (c_handle, "system:administrators", ADMPTSGROUPNAMETOOLONG,
1946              &adminId, &tst)) {
1947             goto fail_IsAdministrator;
1948         }
1949         tst =
1950             ubik_PR_IsAMemberOf(c_handle->pts, 0, userId, adminId,
1951                       &isAdmin);
1952         if (tst != 0) {
1953             goto fail_IsAdministrator;
1954         }
1955         if (isAdmin) {
1956             *admin = 1;
1957         }
1958     }
1959     rc = 1;
1960
1961   fail_IsAdministrator:
1962
1963     if (st != NULL) {
1964         *st = tst;
1965     }
1966     return rc;
1967 }
1968
1969 /*
1970  * pts_UserGet - retrieve information about a particular user.
1971  *
1972  * PARAMETERS
1973  *
1974  * IN cellHandle - a previously opened cellHandle that corresponds
1975  * to the cell where the group exists.
1976  *
1977  * IN userName - the name of the user to retrieve.
1978  *
1979  * OUT userP - a pointer to a pts_UserEntry_t that is filled upon successful
1980  * completion.
1981  *
1982  * LOCKS
1983  *
1984  * No locks are held by this function
1985  *
1986  * RETURN CODES
1987  *
1988  * Returns != 0 upon successful completion.
1989  *
1990  */
1991
1992 int ADMINAPI
1993 pts_UserGet(const void *cellHandle, const char *userName,
1994             pts_UserEntry_p userP, afs_status_p st)
1995 {
1996     int rc = 0;
1997     afs_status_t tst = 0;
1998     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1999     struct prcheckentry userEntry;
2000     afs_int32 userId = 0;
2001     afs_int32 flags;
2002     afs_int32 twobit;
2003     idlist ids;
2004     afs_int32 ptsids[2];
2005     namelist names;
2006     int admin = 0;
2007
2008
2009     /*
2010      * Validate arguments
2011      */
2012
2013     if (!IsValidCellHandle(c_handle, &tst)) {
2014         goto fail_pts_UserGet;
2015     }
2016
2017     if ((userName == NULL) || (*userName == 0)) {
2018         tst = ADMPTSUSERNAMENULL;
2019         goto fail_pts_UserGet;
2020     }
2021
2022     if (userP == NULL) {
2023         tst = ADMPTSUSERPNULL;
2024         goto fail_pts_UserGet;
2025     }
2026
2027     /*
2028      * Translate the group name into an id.
2029      */
2030
2031     if (!TranslateOneName
2032         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2033         goto fail_pts_UserGet;
2034     }
2035
2036     /*
2037      * Retrieve information about the group
2038      */
2039
2040     tst = ubik_PR_ListEntry(c_handle->pts, 0, userId, &userEntry);
2041
2042     if (tst != 0) {
2043         goto fail_pts_UserGet;
2044     }
2045
2046     userP->groupMembershipCount = userEntry.count;
2047     userP->groupCreationQuota = userEntry.ngroups;
2048     /*
2049      * The administrator id, or any member of "system:administrators"
2050      * has unlimited group creation quota.  Denote this by setting
2051      * quota to -1.
2052      */
2053
2054     if (!IsAdministrator(c_handle, userEntry.id, &admin, &tst)) {
2055         goto fail_pts_UserGet;
2056     }
2057
2058     if (admin != 0) {
2059         userP->groupCreationQuota = -1;
2060     }
2061
2062     userP->nameUid = userEntry.id;
2063     userP->ownerUid = userEntry.owner;
2064     userP->creatorUid = userEntry.creator;
2065     strncpy(userP->name, userEntry.name, PTS_MAX_NAME_LEN);
2066     userP->name[PTS_MAX_NAME_LEN - 1] = '\0';
2067
2068     /*
2069      * The permission bits are described in the GroupGet function above.
2070      * The user entry only uses 3 of the 5 permissions, so we shift
2071      * past the unused entries.
2072      */
2073
2074     flags = userEntry.flags;
2075     flags = flags >> 3;
2076     twobit = flags & 3;
2077
2078     GetUserAccess(&userP->listMembership, twobit);
2079
2080     flags = flags >> 2;
2081
2082     if (flags & 1) {
2083         userP->listGroupsOwned = PTS_USER_ANYUSER_ACCESS;
2084     } else {
2085         userP->listGroupsOwned = PTS_USER_OWNER_ACCESS;
2086     }
2087
2088     flags = flags >> 1;
2089     twobit = flags & 3;
2090
2091     GetUserAccess(&userP->listStatus, twobit);
2092
2093     /* 
2094      * Make another rpc and translate the owner and creator ids into
2095      * character strings.
2096      */
2097
2098     ids.idlist_len = 2;
2099     ids.idlist_val = ptsids;
2100     ptsids[0] = userEntry.owner;
2101     ptsids[1] = userEntry.creator;
2102     names.namelist_len = 0;
2103     names.namelist_val = 0;
2104
2105
2106     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
2107         goto fail_pts_UserGet;
2108     }
2109
2110     strncpy(userP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
2111     userP->owner[PTS_MAX_NAME_LEN - 1] ='\0';
2112     strncpy(userP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
2113     userP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
2114     free(names.namelist_val);
2115     rc = 1;
2116
2117   fail_pts_UserGet:
2118
2119     if (st != NULL) {
2120         *st = tst;
2121     }
2122     return rc;
2123 }
2124
2125 /*
2126  * pts_UserRename - rename a user.
2127  *
2128  * PARAMETERS
2129  *
2130  * IN cellHandle - a previously opened cellHandle that corresponds
2131  * to the cell where the group exists.
2132  *
2133  * IN oldName - the name of the user to rename.
2134  *
2135  * IN newName - the new user name.
2136  *
2137  * LOCKS
2138  *
2139  * No locks are held by this function
2140  *
2141  * RETURN CODES
2142  *
2143  * Returns != 0 upon successful completion.
2144  *
2145  */
2146
2147 int ADMINAPI
2148 pts_UserRename(const void *cellHandle, const char *oldName,
2149                char *newName, afs_status_p st)
2150 {
2151     int rc = 0;
2152     afs_status_t tst = 0;
2153     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2154     afs_int32 userId = 0;
2155
2156     /*
2157      * Validate arguments
2158      */
2159
2160     if (!IsValidCellHandle(c_handle, &tst)) {
2161         goto fail_pts_UserRename;
2162     }
2163
2164     if ((oldName == NULL) || (*oldName == 0)) {
2165         tst = ADMPTSOLDNAMENULL;
2166         goto fail_pts_UserRename;
2167     }
2168
2169     if ((newName == NULL) || (*newName == 0)) {
2170         tst = ADMPTSNEWNAMENULL;
2171         goto fail_pts_UserRename;
2172     }
2173
2174     /*
2175      * Translate the user name into an id.
2176      */
2177
2178     if (!TranslateOneName
2179         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &userId, &tst)) {
2180         goto fail_pts_UserRename;
2181     }
2182
2183     /*
2184      * Make the rpc
2185      */
2186
2187     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, userId, newName, 0, 0);
2188
2189     if (tst != 0) {
2190         goto fail_pts_UserRename;
2191     }
2192     rc = 1;
2193
2194   fail_pts_UserRename:
2195
2196     if (st != NULL) {
2197         *st = tst;
2198     }
2199     return rc;
2200 }
2201
2202 /*
2203  * SetUserAccess - translate our Access notation to pts flags.
2204  *
2205  * PARAMETERS
2206  *
2207  * IN userP - the user structure that contains the new permissions.
2208  *
2209  * OUT flags - a pointer to an afs_int32 structure that 
2210  * contains the flags to pass to pts.
2211  *
2212  * LOCKS
2213  *
2214  * No locks are held by this function
2215  *
2216  * RETURN CODES
2217  *
2218  * Returns != 0 upon successful completion.
2219  *
2220  */
2221
2222 static int
2223 SetUserAccess(const pts_UserUpdateEntry_p userP, afs_int32 * flags,
2224               afs_status_p st)
2225 {
2226     int rc = 0;
2227     afs_status_t tst = 0;
2228
2229     *flags = 0;
2230
2231     if (userP->listMembership == PTS_USER_ANYUSER_ACCESS) {
2232         *flags |= 16;
2233     }
2234
2235     if (userP->listGroupsOwned == PTS_USER_ANYUSER_ACCESS) {
2236         *flags |= 32;
2237     }
2238
2239     if (userP->listStatus == PTS_USER_ANYUSER_ACCESS) {
2240         *flags |= 128;
2241     }
2242     rc = 1;
2243
2244     if (st != NULL) {
2245         *st = tst;
2246     }
2247     return rc;
2248 }
2249
2250
2251 /*
2252  * pts_UserModify - update a user entry.
2253  *
2254  * PARAMETERS
2255  *
2256  * IN cellHandle - a previously opened cellHandle that corresponds
2257  * to the cell where the group exists.
2258  *
2259  * IN userName - the name of the user to update.
2260  *
2261  * IN newEntryP - a pointer to a pts_UserUpdateEntry_t that contains the
2262  * new information for user.
2263  *
2264  * LOCKS
2265  *
2266  * No locks are held by this function
2267  *
2268  * RETURN CODES
2269  *
2270  * Returns != 0 upon successful completion.
2271  *
2272  */
2273
2274 int ADMINAPI
2275 pts_UserModify(const void *cellHandle, const char *userName,
2276                const pts_UserUpdateEntry_p newEntryP, afs_status_p st)
2277 {
2278     int rc = 0;
2279     afs_status_t tst = 0;
2280     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2281     afs_int32 userId = 0;
2282     afs_int32 newQuota = 0;
2283     afs_int32 mask = 0;
2284     afs_int32 flags = 0;
2285
2286     /*
2287      * Validate arguments
2288      */
2289
2290     if (!IsValidCellHandle(c_handle, &tst)) {
2291         goto fail_pts_UserModify;
2292     }
2293
2294     if ((userName == NULL) || (*userName == 0)) {
2295         tst = ADMPTSUSERNAMENULL;
2296         goto fail_pts_UserModify;
2297     }
2298
2299     if (newEntryP == NULL) {
2300         tst = ADMPTSNEWENTRYPNULL;
2301         goto fail_pts_UserModify;
2302     }
2303
2304     /*
2305      * Translate the user name into an id.
2306      */
2307
2308     if (!TranslateOneName
2309         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2310         goto fail_pts_UserModify;
2311     }
2312
2313
2314     if (newEntryP->flag & PTS_USER_UPDATE_GROUP_CREATE_QUOTA) {
2315         mask |= PR_SF_NGROUPS;
2316         newQuota = newEntryP->groupCreationQuota;
2317     }
2318
2319     if (newEntryP->flag & PTS_USER_UPDATE_PERMISSIONS) {
2320         mask |= PR_SF_ALLBITS;
2321         if (!SetUserAccess(newEntryP, &flags, &tst)) {
2322             goto fail_pts_UserModify;
2323         }
2324     }
2325
2326     /*
2327      * Make the rpc
2328      */
2329
2330     tst =
2331         ubik_PR_SetFieldsEntry(c_handle->pts, 0, userId, mask, flags,
2332                   newQuota, 0, 0, 0);
2333
2334     if (tst != 0) {
2335         goto fail_pts_UserModify;
2336     }
2337     rc = 1;
2338
2339   fail_pts_UserModify:
2340
2341     if (st != NULL) {
2342         *st = tst;
2343     }
2344     return rc;
2345 }
2346
2347 /*
2348  * pts_UserMaxGet - get the maximum in use user id.
2349  *
2350  * PARAMETERS
2351  *
2352  * IN cellHandle - a previously opened cellHandle that corresponds
2353  * to the cell where the group exists.
2354  *
2355  * OUT maxUserId - upon successful completion contains the max in use id.
2356  *
2357  * LOCKS
2358  *
2359  * No locks are held by this function
2360  *
2361  * RETURN CODES
2362  *
2363  * Returns != 0 upon successful completion.
2364  *
2365  */
2366
2367 int ADMINAPI
2368 pts_UserMaxGet(const void *cellHandle, int *maxUserId, afs_status_p st)
2369 {
2370     int rc = 0;
2371     afs_status_t tst = 0;
2372     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2373     afs_int32 maxGroupId = 0;
2374
2375     /*
2376      * Validate arguments
2377      */
2378
2379     if (!IsValidCellHandle(c_handle, &tst)) {
2380         goto fail_pts_UserMaxGet;
2381     }
2382
2383     if (maxUserId == NULL) {
2384         tst = ADMPTSMAXUSERIDNULL;
2385         goto fail_pts_UserMaxGet;
2386     }
2387
2388     tst = ubik_PR_ListMax(c_handle->pts, 0, maxUserId, &maxGroupId);
2389
2390     if (tst != 0) {
2391         goto fail_pts_UserMaxGet;
2392     }
2393     rc = 1;
2394
2395   fail_pts_UserMaxGet:
2396
2397     if (st != NULL) {
2398         *st = tst;
2399     }
2400     return rc;
2401 }
2402
2403 /*
2404  * pts_UserMaxSet - set the maximum user id.
2405  *
2406  * PARAMETERS
2407  *
2408  * IN cellHandle - a previously opened cellHandle that corresponds
2409  * to the cell where the group exists.
2410  *
2411  * IN maxUserId - the new max user id.
2412  *
2413  * LOCKS
2414  *
2415  * No locks are held by this function
2416  *
2417  * RETURN CODES
2418  *
2419  * Returns != 0 upon successful completion.
2420  *
2421  */
2422
2423 int ADMINAPI
2424 pts_UserMaxSet(const void *cellHandle, int maxUserId, afs_status_p st)
2425 {
2426     int rc = 0;
2427     afs_status_t tst = 0;
2428     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2429
2430     /*
2431      * Validate arguments
2432      */
2433
2434     if (!IsValidCellHandle(c_handle, &tst)) {
2435         goto fail_pts_UserMaxSet;
2436     }
2437
2438     tst = ubik_PR_SetMax(c_handle->pts, 0, maxUserId, 0);
2439
2440     if (tst != 0) {
2441         goto fail_pts_UserMaxSet;
2442     }
2443     rc = 1;
2444
2445   fail_pts_UserMaxSet:
2446
2447     if (st != NULL) {
2448         *st = tst;
2449     }
2450     return rc;
2451 }
2452
2453 /*
2454  * pts_UserMemberListBegin - begin iterating over the list of groups
2455  * a particular user belongs to.
2456  *
2457  * PARAMETERS
2458  *
2459  * IN cellHandle - a previously opened cellHandle that corresponds
2460  * to the cell where the group exists.
2461  *
2462  * IN groupName - the group whose members will be returned.
2463  *
2464  * OUT iterationIdP - upon successful completion contains a iterator that
2465  * can be passed to pts_GroupMemberListNext.
2466  *
2467  * LOCKS
2468  *
2469  * No locks are obtained or released by this function
2470  *
2471  * RETURN CODES
2472  *
2473  * Returns != 0 upon successful completion.
2474  *
2475  */
2476
2477 int ADMINAPI
2478 pts_UserMemberListBegin(const void *cellHandle, const char *userName,
2479                         void **iterationIdP, afs_status_p st)
2480 {
2481     return MemberListBegin(cellHandle, userName, ADMPTSUSERNAMENULL,
2482                            ADMPTSUSERNAMETOOLONG, iterationIdP, st);
2483
2484 }
2485
2486 /*
2487  * pts_UserMemberListNext - get the next group a user belongs to
2488  *
2489  * PARAMETERS
2490  *
2491  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2492  *
2493  * OUT userName - upon successful completion contains the next group a user
2494  * belongs to.
2495  *
2496  * LOCKS
2497  *
2498  * The iterator mutex is held during the retrieval of the next member.
2499  *
2500  * RETURN CODES
2501  *
2502  * Returns != 0 upon successful completion.
2503  *
2504  */
2505
2506 int ADMINAPI
2507 pts_UserMemberListNext(const void *iterationId, char *userName,
2508                        afs_status_p st)
2509 {
2510     return pts_GroupMemberListNext(iterationId, userName, st);
2511 }
2512
2513 /*
2514  * pts_UserMemberListDone - finish using a user list iterator
2515  *
2516  * PARAMETERS
2517  *
2518  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2519  *
2520  * LOCKS
2521  *
2522  * The iterator is locked and then destroyed
2523  *
2524  * RETURN CODES
2525  *
2526  * Returns != 0 upon successful completion.
2527  *
2528  * ASSUMPTIONS
2529  *
2530  * It is the user's responsibility to make sure pts_UserMemberListDone
2531  * is called only once for each iterator.
2532  */
2533
2534 int ADMINAPI
2535 pts_UserMemberListDone(const void *iterationId, afs_status_p st)
2536 {
2537     return pts_GroupMemberListDone(iterationId, st);
2538 }
2539
2540 typedef struct owned_group_list {
2541     namelist owned_names;       /* the list of character names owned by this id */
2542     prlist owned_ids;           /* the list of pts ids owned by this id */
2543     afs_int32 index;            /* the index into owned_names for the next group */
2544     afs_int32 owner;            /* the pts id of the owner */
2545     afs_int32 more;             /* the last parameter to PR_ListOwned */
2546     int finished_retrieving;    /* set when we've processed the last owned_names */
2547     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2548     char group[CACHED_ITEMS][PTS_MAX_NAME_LEN]; /* cache of names */
2549 } owned_group_list_t, *owned_group_list_p;
2550
2551 static int
2552 DeleteOwnedGroupSpecificData(void *rpc_specific, afs_status_p st)
2553 {
2554     int rc = 0;
2555     afs_status_t tst = 0;
2556     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2557
2558     if (list->owned_names.namelist_val != NULL) {
2559         free(list->owned_names.namelist_val);
2560     }
2561
2562     if (list->owned_ids.prlist_val != NULL) {
2563         free(list->owned_ids.prlist_val);
2564     }
2565     rc = 1;
2566
2567     if (st != NULL) {
2568         *st = tst;
2569     }
2570     return rc;
2571 }
2572
2573 static int
2574 GetOwnedGroupRPC(void *rpc_specific, int slot, int *last_item,
2575                  int *last_item_contains_data, afs_status_p st)
2576 {
2577     int rc = 0;
2578     afs_status_t tst = 0;
2579     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2580
2581     /*
2582      * We really don't make an rpc for every entry we return here
2583      * since the pts interface allows several members to be returned
2584      * with one rpc, but we fake it to make the iterator happy.
2585      */
2586
2587     /*
2588      * Check to see if we are done retrieving data
2589      */
2590
2591     if ((list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2592         *last_item = 1;
2593         *last_item_contains_data = 0;
2594         goto fail_GetOwnedGroupRPC;
2595     }
2596
2597     /*
2598      * Check to see if we really need to make an rpc
2599      */
2600
2601     if ((!list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2602         tst =
2603             ubik_PR_ListOwned(list->c_handle->pts, 0, list->owner,
2604                       &list->owned_ids, &list->more);
2605         if (tst != 0) {
2606             goto fail_GetOwnedGroupRPC;
2607         }
2608
2609         if (!TranslatePTSIds
2610             (list->c_handle, &list->owned_names, (idlist *) & list->owned_ids,
2611              &tst)) {
2612             goto fail_GetOwnedGroupRPC;
2613         }
2614         list->index = 0;
2615
2616         if (list->owned_names.namelist_val == NULL) {
2617             *last_item = 1;
2618             *last_item_contains_data = 0;
2619             goto fail_GetOwnedGroupRPC;
2620         }
2621     }
2622
2623     /*
2624      * We can retrieve the next group from data we already received
2625      */
2626
2627     strcpy(list->group[slot], list->owned_names.namelist_val[list->index]);
2628     list->index++;
2629
2630     /*
2631      * Check to see if there is more data to be retrieved
2632      * We need to free up the previously retrieved data here
2633      * and then check to see if the last rpc indicated that there
2634      * were more items to retrieve.
2635      */
2636
2637     if (list->index >= list->owned_names.namelist_len) {
2638         list->owned_names.namelist_len = 0;
2639         free(list->owned_names.namelist_val);
2640         list->owned_names.namelist_val = 0;
2641
2642         list->owned_ids.prlist_len = 0;
2643         free(list->owned_ids.prlist_val);
2644         list->owned_ids.prlist_val = 0;
2645
2646         if (!list->more) {
2647             list->finished_retrieving = 1;
2648         }
2649     }
2650     rc = 1;
2651
2652   fail_GetOwnedGroupRPC:
2653
2654     if (st != NULL) {
2655         *st = tst;
2656     }
2657     return rc;
2658 }
2659
2660 static int
2661 GetOwnedGroupFromCache(void *rpc_specific, int slot, void *dest,
2662                        afs_status_p st)
2663 {
2664     int rc = 0;
2665     afs_status_t tst = 0;
2666     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2667
2668     strcpy((char *)dest, list->group[slot]);
2669     rc = 1;
2670
2671     if (st != NULL) {
2672         *st = tst;
2673     }
2674
2675     return rc;
2676 }
2677
2678 /*
2679  * pts_OwnedGroupListBegin - begin iterating over the list of groups
2680  * a particular user owns.
2681  *
2682  * PARAMETERS
2683  *
2684  * IN cellHandle - a previously opened cellHandle that corresponds
2685  * to the cell where the group exists.
2686  *
2687  * IN ownerName - the owner of the groups of interest.
2688  *
2689  * OUT iterationIdP - upon successful completion contains a iterator that
2690  * can be passed to pts_OwnedGroupListNext.
2691  *
2692  * LOCKS
2693  *
2694  * No locks are held by this function
2695  *
2696  * RETURN CODES
2697  *
2698  * Returns != 0 upon successful completion.
2699  *
2700  */
2701
2702 int ADMINAPI
2703 pts_OwnedGroupListBegin(const void *cellHandle, const char *userName,
2704                         void **iterationIdP, afs_status_p st)
2705 {
2706     int rc = 0;
2707     afs_status_t tst = 0;
2708     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2709     afs_admin_iterator_p iter =
2710         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
2711     owned_group_list_p list =
2712         (owned_group_list_p) 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 =
3047         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3048     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3049
3050     /*
3051      * Validate arguments
3052      */
3053
3054     if (!IsValidCellHandle(c_handle, &tst)) {
3055         goto fail_pts_UserListBegin;
3056     }
3057
3058     if (iterationIdP == NULL) {
3059         tst = ADMITERATORNULL;
3060         goto fail_pts_UserListBegin;
3061     }
3062
3063     if ((iter == NULL) || (list == NULL)) {
3064         tst = ADMNOMEM;
3065         goto fail_pts_UserListBegin;
3066     }
3067
3068     /*
3069      * Initialize the iterator specific data
3070      */
3071
3072     list->index = 0;
3073     list->finished_retrieving = 0;
3074     list->c_handle = c_handle;
3075     list->names = NULL;
3076     list->nextstartindex = 0;
3077     list->nentries = 0;
3078     list->flag = PRUSERS;
3079
3080     if (IteratorInit
3081         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3082          DeletePTSSpecificData, &tst)) {
3083         *iterationIdP = (void *)iter;
3084         rc = 1;
3085     }
3086
3087   fail_pts_UserListBegin:
3088
3089     if (rc == 0) {
3090         if (iter != NULL) {
3091             free(iter);
3092         }
3093         if (list != NULL) {
3094             free(list);
3095         }
3096     }
3097
3098     if (st != NULL) {
3099         *st = tst;
3100     }
3101     return rc;
3102 }
3103
3104 /*
3105  * pts_UserListNext - get the next user in the cell.
3106  *
3107  * PARAMETERS
3108  *
3109  * IN iterationId - an iterator previously returned by pts_UserListBegin
3110  *
3111  * OUT groupName - upon successful completion contains the next user
3112  *
3113  * LOCKS
3114  *
3115  * The iterator mutex is held during the retrieval of the next member.
3116  *
3117  * RETURN CODES
3118  *
3119  * Returns != 0 upon successful completion.
3120  *
3121  */
3122
3123 int ADMINAPI
3124 pts_UserListNext(const void *iterationId, char *userName, afs_status_p st)
3125 {
3126     int rc = 0;
3127     afs_status_t tst = 0;
3128     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3129
3130     /*
3131      * Validate arguments
3132      */
3133
3134     if (iterationId == NULL) {
3135         tst = ADMITERATORNULL;
3136         goto fail_pts_UserListNext;
3137     }
3138
3139     if (userName == NULL) {
3140         tst = ADMPTSUSERNAMENULL;
3141         goto fail_pts_UserListNext;
3142     }
3143
3144     rc = IteratorNext(iter, (void *)userName, &tst);
3145
3146   fail_pts_UserListNext:
3147
3148     if (st != NULL) {
3149         *st = tst;
3150     }
3151     return rc;
3152 }
3153
3154 /*
3155  * pts_UserListDone - finish using a user list iterator
3156  *
3157  * PARAMETERS
3158  *
3159  * IN iterationId - an iterator previously returned by pts_UserListBegin
3160  *
3161  * LOCKS
3162  *
3163  * The iterator is locked and then destroyed
3164  *
3165  * RETURN CODES
3166  *
3167  * Returns != 0 upon successful completion.
3168  *
3169  * ASSUMPTIONS
3170  *
3171  * It is the user's responsibility to make sure pts_UserListDone
3172  * is called only once for each iterator.
3173  */
3174
3175 int ADMINAPI
3176 pts_UserListDone(const void *iterationId, afs_status_p st)
3177 {
3178     int rc = 0;
3179     afs_status_t tst = 0;
3180     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3181
3182     /*
3183      * Validate arguments
3184      */
3185
3186     if (iterationId == NULL) {
3187         tst = ADMITERATORNULL;
3188         goto fail_pts_UserListDone;
3189     }
3190
3191     rc = IteratorDone(iter, &tst);
3192
3193   fail_pts_UserListDone:
3194
3195     if (st != NULL) {
3196         *st = tst;
3197     }
3198     return rc;
3199 }
3200
3201 /*
3202  * pts_GroupListBegin - begin iterating over the list of groups
3203  * in a particular cell.
3204  *
3205  * PARAMETERS
3206  *
3207  * IN cellHandle - a previously opened cellHandle that corresponds
3208  * to the cell where the groups exist.
3209  *
3210  * OUT iterationIdP - upon successful completion contains a iterator that
3211  * can be passed to pts_GroupListNext.
3212  *
3213  * LOCKS
3214  *
3215  * No locks are held by this function
3216  *
3217  * RETURN CODES
3218  *
3219  * Returns != 0 upon successful completion.
3220  *
3221  */
3222
3223 int ADMINAPI
3224 pts_GroupListBegin(const void *cellHandle, void **iterationIdP,
3225                    afs_status_p st)
3226 {
3227     int rc = 0;
3228     afs_status_t tst = 0;
3229     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3230     afs_admin_iterator_p iter =
3231         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3232     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3233
3234     /*
3235      * Validate arguments
3236      */
3237
3238     if (!IsValidCellHandle(c_handle, &tst)) {
3239         goto fail_pts_GroupListBegin;
3240     }
3241
3242     if (iterationIdP == NULL) {
3243         tst = ADMITERATORNULL;
3244         goto fail_pts_GroupListBegin;
3245     }
3246
3247     if ((iter == NULL) || (list == NULL)) {
3248         tst = ADMNOMEM;
3249         goto fail_pts_GroupListBegin;
3250     }
3251
3252     /*
3253      * Initialize the iterator specific data
3254      */
3255
3256     list->index = 0;
3257     list->finished_retrieving = 0;
3258     list->c_handle = c_handle;
3259     list->names = NULL;
3260     list->nextstartindex = 0;
3261     list->nentries = 0;
3262     list->flag = PRGROUPS;
3263
3264     if (IteratorInit
3265         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3266          DeletePTSSpecificData, &tst)) {
3267         *iterationIdP = (void *)iter;
3268         rc = 1;
3269     }
3270
3271   fail_pts_GroupListBegin:
3272
3273     if (rc == 0) {
3274         if (iter != NULL) {
3275             free(iter);
3276         }
3277         if (list != NULL) {
3278             free(list);
3279         }
3280     }
3281
3282     if (st != NULL) {
3283         *st = tst;
3284     }
3285     return rc;
3286 }
3287
3288 /*
3289  * pts_UserListNext - get the next group in a cell.
3290  *
3291  * PARAMETERS
3292  *
3293  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3294  *
3295  * OUT groupName - upon successful completion contains the next group
3296  *
3297  * LOCKS
3298  *
3299  * The iterator mutex is held during the retrieval of the next member.
3300  *
3301  * RETURN CODES
3302  *
3303  * Returns != 0 upon successful completion.
3304  *
3305  */
3306
3307 int ADMINAPI
3308 pts_GroupListNext(const void *iterationId, char *groupName, afs_status_p st)
3309 {
3310     int rc = 0;
3311     afs_status_t tst = 0;
3312     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3313
3314     /*
3315      * Validate arguments
3316      */
3317
3318     if (iterationId == NULL) {
3319         tst = ADMITERATORNULL;
3320         goto fail_pts_GroupListNext;
3321     }
3322
3323     if (groupName == NULL) {
3324         tst = ADMPTSGROUPNAMENULL;
3325         goto fail_pts_GroupListNext;
3326     }
3327
3328     rc = IteratorNext(iter, (void *)groupName, &tst);
3329
3330   fail_pts_GroupListNext:
3331
3332     if (st != NULL) {
3333         *st = tst;
3334     }
3335     return rc;
3336 }
3337
3338 /*
3339  * pts_GroupListDone - finish using a group list iterator
3340  *
3341  * PARAMETERS
3342  *
3343  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3344  *
3345  * LOCKS
3346  *
3347  * The iterator is locked and then destroyed
3348  *
3349  * RETURN CODES
3350  *
3351  * Returns != 0 upon successful completion.
3352  *
3353  * ASSUMPTIONS
3354  *
3355  * It is the user's responsibility to make sure pts_GroupListDone
3356  * is called only once for each iterator.
3357  */
3358
3359 int ADMINAPI
3360 pts_GroupListDone(const void *iterationId, afs_status_p st)
3361 {
3362     int rc = 0;
3363     afs_status_t tst = 0;
3364     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3365
3366     /*
3367      * Validate arguments
3368      */
3369
3370     if (iterationId == NULL) {
3371         tst = ADMITERATORNULL;
3372         goto fail_pts_GroupListDone;
3373     }
3374
3375     rc = IteratorDone(iter, &tst);
3376
3377   fail_pts_GroupListDone:
3378
3379     if (st != NULL) {
3380         *st = tst;
3381     }
3382     return rc;
3383 }