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