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