342a9d3496ce9acbc9b5cd6bfc83382f8f66893b
[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     /*
1483      * Validate arguments
1484      */
1485
1486     if (!IsValidCellHandle(c_handle, &tst)) {
1487         goto fail_pts_GroupMemberRemove;
1488     }
1489
1490     if ((userName == NULL) || (*userName == 0)) {
1491         tst = ADMPTSUSERNAMENULL;
1492         goto fail_pts_GroupMemberRemove;
1493     }
1494
1495     if ((groupName == NULL) || (*groupName == 0)) {
1496         tst = ADMPTSGROUPNAMENULL;
1497         goto fail_pts_GroupMemberRemove;
1498     }
1499
1500     if (!TranslateTwoNames
1501         (c_handle, userName, ADMPTSUSERNAMETOOLONG, groupName,
1502          ADMPTSGROUPNAMETOOLONG, &ids, &tst)) {
1503         goto fail_pts_GroupMemberRemove;
1504     }
1505
1506     /*
1507      * Make the rpc
1508      */
1509
1510     tst =
1511         ubik_PR_RemoveFromGroup(c_handle->pts, 0, ids.idlist_val[0],
1512                   ids.idlist_val[1]);
1513
1514     if (tst != 0) {
1515         goto fail_pts_GroupMemberRemove;
1516     }
1517     rc = 1;
1518
1519   fail_pts_GroupMemberRemove:
1520
1521     if (ids.idlist_val != 0) {
1522         free(ids.idlist_val);
1523     }
1524
1525     if (st != NULL) {
1526         *st = tst;
1527     }
1528     return rc;
1529 }
1530
1531 /*
1532  * pts_GroupRename - change the name of a group
1533  *
1534  * PARAMETERS
1535  *
1536  * IN cellHandle - a previously opened cellHandle that corresponds
1537  * to the cell where the group exists.
1538  *
1539  * IN oldName - the current group name
1540  *
1541  * IN newName - the new group name
1542  *
1543  * LOCKS
1544  *
1545  * No locks are held by this function
1546  *
1547  * RETURN CODES
1548  *
1549  * Returns != 0 upon successful completion.
1550  *
1551  */
1552
1553 int ADMINAPI
1554 pts_GroupRename(const void *cellHandle, const char *oldName,
1555                 char *newName, afs_status_p st)
1556 {
1557     int rc = 0;
1558     afs_status_t tst = 0;
1559     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1560     afs_int32 groupId = 0;
1561
1562     /*
1563      * Validate arguments
1564      */
1565
1566     if (!IsValidCellHandle(c_handle, &tst)) {
1567         goto fail_pts_GroupRename;
1568     }
1569
1570     if ((newName == NULL) || (*newName == 0)) {
1571         tst = ADMPTSNEWNAMENULL;
1572         goto fail_pts_GroupRename;
1573     }
1574
1575     if ((oldName == NULL) || (*oldName == 0)) {
1576         tst = ADMPTSOLDNAMENULL;
1577         goto fail_pts_GroupRename;
1578     }
1579
1580     /*
1581      * Translate the group name into an id.
1582      */
1583
1584     if (!TranslateOneName
1585         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &groupId, &tst)) {
1586         goto fail_pts_GroupRename;
1587     }
1588
1589     /*
1590      * Make the rpc
1591      */
1592
1593     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, groupId, newName, 0, 0);
1594
1595     if (tst != 0) {
1596         goto fail_pts_GroupRename;
1597     }
1598     rc = 1;
1599
1600   fail_pts_GroupRename:
1601
1602     if (st != NULL) {
1603         *st = tst;
1604     }
1605     return rc;
1606 }
1607
1608 /*
1609  * SetGroupAccess - translate our Access notation to pts flags.
1610  *
1611  * PARAMETERS
1612  *
1613  * IN rights - the permissions.
1614  *
1615  * OUT flags - a pointer to an afs_int32 structure that 
1616  * contains the flags to pass to pts.
1617  *
1618  * LOCKS
1619  *
1620  * No locks are held by this function
1621  *
1622  * RETURN CODES
1623  *
1624  * Returns != 0 upon successful completion.
1625  *
1626  */
1627
1628 static int
1629 SetGroupAccess(const pts_GroupUpdateEntry_p rights, afs_int32 * flags,
1630                afs_status_p st)
1631 {
1632     int rc = 0;
1633     afs_status_t tst = 0;
1634
1635     *flags = 0;
1636
1637     if (rights->listDelete == PTS_GROUP_ACCESS) {
1638         *flags |= 1;
1639     } else if (rights->listDelete == PTS_GROUP_ANYUSER_ACCESS) {
1640         tst = ADMPTSINVALIDGROUPDELETEPERM;
1641         goto fail_SetGroupAccess;
1642     }
1643
1644     if (rights->listAdd == PTS_GROUP_ACCESS) {
1645         *flags |= 2;
1646     } else if (rights->listAdd == PTS_GROUP_ANYUSER_ACCESS) {
1647         *flags |= 4;
1648     }
1649
1650     if (rights->listMembership == PTS_GROUP_ACCESS) {
1651         *flags |= 8;
1652     } else if (rights->listMembership == PTS_GROUP_ANYUSER_ACCESS) {
1653         *flags |= 16;
1654     }
1655
1656     if (rights->listGroupsOwned == PTS_GROUP_ANYUSER_ACCESS) {
1657         *flags |= 32;
1658     } else if (rights->listGroupsOwned == PTS_GROUP_ACCESS) {
1659         tst = ADMPTSINVALIDGROUPSOWNEDPERM;
1660         goto fail_SetGroupAccess;
1661     }
1662
1663     if (rights->listStatus == PTS_GROUP_ACCESS) {
1664         *flags |= 64;
1665     } else if (rights->listStatus == PTS_GROUP_ANYUSER_ACCESS) {
1666         *flags |= 128;
1667     }
1668     rc = 1;
1669
1670   fail_SetGroupAccess:
1671
1672     if (st != NULL) {
1673         *st = tst;
1674     }
1675     return rc;
1676 }
1677
1678 /*
1679  * pts_GroupModify - change the contents of a group entry.
1680  *
1681  * PARAMETERS
1682  *
1683  * IN cellHandle - a previously opened cellHandle that corresponds
1684  * to the cell where the group exists.
1685  *
1686  * IN groupName - the group to change
1687  *
1688  * OUT newEntryP - a pointer to a pts_GroupUpdateEntry_t structure that 
1689  * contains the new information for the group.
1690  *
1691  * LOCKS
1692  *
1693  * No locks are held by this function
1694  *
1695  * RETURN CODES
1696  *
1697  * Returns != 0 upon successful completion.
1698  *
1699  */
1700
1701 int ADMINAPI
1702 pts_GroupModify(const void *cellHandle, const char *groupName,
1703                 const pts_GroupUpdateEntry_p newEntryP, afs_status_p st)
1704 {
1705     int rc = 0;
1706     afs_status_t tst = 0;
1707     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1708     afs_int32 groupId = 0;
1709     afs_int32 flags = 0;
1710
1711     /*
1712      * Validate arguments
1713      */
1714
1715     if (!IsValidCellHandle(c_handle, &tst)) {
1716         goto fail_pts_GroupModify;
1717     }
1718
1719     if ((groupName == NULL) || (*groupName == 0)) {
1720         tst = ADMPTSGROUPNAMENULL;
1721         goto fail_pts_GroupModify;
1722     }
1723
1724
1725     if (newEntryP == NULL) {
1726         tst = ADMPTSNEWENTRYPNULL;
1727         goto fail_pts_GroupModify;
1728     }
1729
1730     /*
1731      * Translate the group name into an id.
1732      */
1733
1734     if (!TranslateOneName
1735         (c_handle, groupName, ADMPTSGROUPNAMETOOLONG, &groupId, &tst)) {
1736         goto fail_pts_GroupModify;
1737     }
1738
1739     /*
1740      * Set the flags argument
1741      */
1742
1743     if (!SetGroupAccess(newEntryP, &flags, &tst)) {
1744         goto fail_pts_GroupModify;
1745     }
1746
1747     /*
1748      * Make the rpc
1749      */
1750
1751     tst =
1752         ubik_PR_SetFieldsEntry(c_handle->pts, 0, groupId, PR_SF_ALLBITS,
1753                   flags, 0, 0, 0, 0);
1754
1755     if (tst != 0) {
1756         goto fail_pts_GroupModify;
1757     }
1758     rc = 1;
1759
1760   fail_pts_GroupModify:
1761
1762     if (st != NULL) {
1763         *st = tst;
1764     }
1765     return rc;
1766 }
1767
1768 /*
1769  * pts_UserCreate - create a new user.
1770  *
1771  * PARAMETERS
1772  *
1773  * IN cellHandle - a previously opened cellHandle that corresponds
1774  * to the cell where the group exists.
1775  *
1776  * IN newUser - the name of the new user.
1777  *
1778  * IN newUserId - the id to assign to the new user.  Pass 0 to have the
1779  * id assigned by pts.
1780  *
1781  * LOCKS
1782  *
1783  * No locks are held by this function
1784  *
1785  * RETURN CODES
1786  *
1787  * Returns != 0 upon successful completion.
1788  *
1789  */
1790
1791 int ADMINAPI
1792 pts_UserCreate(const void *cellHandle, char *userName, int *newUserId,
1793                afs_status_p st)
1794 {
1795     int rc = 0;
1796     afs_status_t tst = 0;
1797     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1798
1799     /*
1800      * Validate arguments
1801      */
1802
1803     if (!IsValidCellHandle(c_handle, &tst)) {
1804         goto fail_pts_UserCreate;
1805     }
1806
1807     if ((userName == NULL) || (*userName == 0)) {
1808         tst = ADMPTSUSERNAMENULL;
1809         goto fail_pts_UserCreate;
1810     }
1811
1812     if (newUserId == NULL) {
1813         tst = ADMPTSNEWUSERIDNULL;
1814         goto fail_pts_UserCreate;
1815     }
1816
1817     /*
1818      * We make a different rpc based upon the input to this function
1819      */
1820
1821     if (*newUserId != 0) {
1822         tst =
1823             ubik_PR_INewEntry(c_handle->pts, 0, userName, *newUserId,
1824                       0);
1825     } else {
1826         tst =
1827             ubik_PR_NewEntry(c_handle->pts, 0, userName, 0, 0,
1828                       newUserId);
1829     }
1830
1831     if (tst != 0) {
1832         goto fail_pts_UserCreate;
1833     }
1834     rc = 1;
1835
1836   fail_pts_UserCreate:
1837
1838     if (st != NULL) {
1839         *st = tst;
1840     }
1841     return rc;
1842 }
1843
1844 /*
1845  * pts_UserDelete - delete a user.
1846  *
1847  * PARAMETERS
1848  *
1849  * IN cellHandle - a previously opened cellHandle that corresponds
1850  * to the cell where the group exists.
1851  *
1852  * IN user - the name of the user to delete.
1853  *
1854  * LOCKS
1855  *
1856  * No locks are held by this function
1857  *
1858  * RETURN CODES
1859  *
1860  * Returns != 0 upon successful completion.
1861  *
1862  */
1863
1864 int ADMINAPI
1865 pts_UserDelete(const void *cellHandle, const char *userName, afs_status_p st)
1866 {
1867     return EntryDelete(cellHandle, userName, ADMPTSUSERNAMENULL,
1868                        ADMPTSUSERNAMETOOLONG, st);
1869 }
1870
1871
1872 /*
1873  * GetUserAccess - a small convenience function for setting
1874  * permissions.
1875  *
1876  * PARAMETERS
1877  *
1878  * IN access - a pointer to a pts_userAccess_t to be set with the
1879  * correct permission.
1880  *
1881  * IN flag - the current permission flag used to derive the permission.
1882  *
1883  * LOCKS
1884  *
1885  * No locks are obtained or released by this function
1886  *
1887  * RETURN CODES
1888  *
1889  * Since this function cannot fail, it returns void.
1890  *
1891  */
1892
1893 static void
1894 GetUserAccess(pts_userAccess_p access, afs_int32 flag)
1895 {
1896
1897     *access = PTS_USER_OWNER_ACCESS;
1898     if (flag == 2) {
1899         *access = PTS_USER_ANYUSER_ACCESS;
1900     }
1901 }
1902
1903 /*
1904  * IsAdministrator - determine if a user is an administrator.
1905  *
1906  * PARAMETERS
1907  *
1908  * IN cellHandle - a previously opened cellHandle that corresponds
1909  * to the cell where the group exists.
1910  *
1911  * IN userEntry - the user data for the user in question.
1912  *
1913  * OUT admin - set to 1 if the user is an administrator, 0 otherwise.
1914  *
1915  * LOCKS
1916  *
1917  * No locks are held by this function
1918  *
1919  * RETURN CODES
1920  *
1921  * Returns != 0 upon successful completion.
1922  *
1923  */
1924
1925 static int
1926 IsAdministrator(const afs_cell_handle_p c_handle, afs_int32 userId,
1927                 int *admin, afs_status_p st)
1928 {
1929     int rc = 0;
1930     afs_status_t tst = 0;
1931     afs_int32 adminId = 0;
1932     afs_int32 isAdmin = 0;
1933
1934     *admin = 0;
1935
1936     if (userId == SYSADMINID) {
1937         *admin = 1;
1938     } else {
1939         if (!TranslateOneName
1940             (c_handle, "system:administrators", ADMPTSGROUPNAMETOOLONG,
1941              &adminId, &tst)) {
1942             goto fail_IsAdministrator;
1943         }
1944         tst =
1945             ubik_PR_IsAMemberOf(c_handle->pts, 0, userId, adminId,
1946                       &isAdmin);
1947         if (tst != 0) {
1948             goto fail_IsAdministrator;
1949         }
1950         if (isAdmin) {
1951             *admin = 1;
1952         }
1953     }
1954     rc = 1;
1955
1956   fail_IsAdministrator:
1957
1958     if (st != NULL) {
1959         *st = tst;
1960     }
1961     return rc;
1962 }
1963
1964 /*
1965  * pts_UserGet - retrieve information about a particular user.
1966  *
1967  * PARAMETERS
1968  *
1969  * IN cellHandle - a previously opened cellHandle that corresponds
1970  * to the cell where the group exists.
1971  *
1972  * IN userName - the name of the user to retrieve.
1973  *
1974  * OUT userP - a pointer to a pts_UserEntry_t that is filled upon successful
1975  * completion.
1976  *
1977  * LOCKS
1978  *
1979  * No locks are held by this function
1980  *
1981  * RETURN CODES
1982  *
1983  * Returns != 0 upon successful completion.
1984  *
1985  */
1986
1987 int ADMINAPI
1988 pts_UserGet(const void *cellHandle, const char *userName,
1989             pts_UserEntry_p userP, afs_status_p st)
1990 {
1991     int rc = 0;
1992     afs_status_t tst = 0;
1993     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
1994     struct prcheckentry userEntry;
1995     afs_int32 userId = 0;
1996     afs_int32 flags;
1997     afs_int32 twobit;
1998     idlist ids;
1999     afs_int32 ptsids[2];
2000     namelist names;
2001     int admin = 0;
2002
2003
2004     /*
2005      * Validate arguments
2006      */
2007
2008     if (!IsValidCellHandle(c_handle, &tst)) {
2009         goto fail_pts_UserGet;
2010     }
2011
2012     if ((userName == NULL) || (*userName == 0)) {
2013         tst = ADMPTSUSERNAMENULL;
2014         goto fail_pts_UserGet;
2015     }
2016
2017     if (userP == NULL) {
2018         tst = ADMPTSUSERPNULL;
2019         goto fail_pts_UserGet;
2020     }
2021
2022     /*
2023      * Translate the group name into an id.
2024      */
2025
2026     if (!TranslateOneName
2027         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2028         goto fail_pts_UserGet;
2029     }
2030
2031     /*
2032      * Retrieve information about the group
2033      */
2034
2035     tst = ubik_PR_ListEntry(c_handle->pts, 0, userId, &userEntry);
2036
2037     if (tst != 0) {
2038         goto fail_pts_UserGet;
2039     }
2040
2041     userP->groupMembershipCount = userEntry.count;
2042     userP->groupCreationQuota = userEntry.ngroups;
2043     /*
2044      * The administrator id, or any member of "system:administrators"
2045      * has unlimited group creation quota.  Denote this by setting
2046      * quota to -1.
2047      */
2048
2049     if (!IsAdministrator(c_handle, userEntry.id, &admin, &tst)) {
2050         goto fail_pts_UserGet;
2051     }
2052
2053     if (admin != 0) {
2054         userP->groupCreationQuota = -1;
2055     }
2056
2057     userP->nameUid = userEntry.id;
2058     userP->ownerUid = userEntry.owner;
2059     userP->creatorUid = userEntry.creator;
2060     strncpy(userP->name, userEntry.name, PTS_MAX_NAME_LEN);
2061     userP->name[PTS_MAX_NAME_LEN - 1] = '\0';
2062
2063     /*
2064      * The permission bits are described in the GroupGet function above.
2065      * The user entry only uses 3 of the 5 permissions, so we shift
2066      * past the unused entries.
2067      */
2068
2069     flags = userEntry.flags;
2070     flags = flags >> 3;
2071     twobit = flags & 3;
2072
2073     GetUserAccess(&userP->listMembership, twobit);
2074
2075     flags = flags >> 2;
2076
2077     if (flags & 1) {
2078         userP->listGroupsOwned = PTS_USER_ANYUSER_ACCESS;
2079     } else {
2080         userP->listGroupsOwned = PTS_USER_OWNER_ACCESS;
2081     }
2082
2083     flags = flags >> 1;
2084     twobit = flags & 3;
2085
2086     GetUserAccess(&userP->listStatus, twobit);
2087
2088     /* 
2089      * Make another rpc and translate the owner and creator ids into
2090      * character strings.
2091      */
2092
2093     ids.idlist_len = 2;
2094     ids.idlist_val = ptsids;
2095     ptsids[0] = userEntry.owner;
2096     ptsids[1] = userEntry.creator;
2097     names.namelist_len = 0;
2098     names.namelist_val = 0;
2099
2100
2101     if (!TranslatePTSIds(c_handle, &names, &ids, &tst)) {
2102         goto fail_pts_UserGet;
2103     }
2104
2105     strncpy(userP->owner, names.namelist_val[0], PTS_MAX_NAME_LEN);
2106     userP->owner[PTS_MAX_NAME_LEN - 1] ='\0';
2107     strncpy(userP->creator, names.namelist_val[1], PTS_MAX_NAME_LEN);
2108     userP->creator[PTS_MAX_NAME_LEN - 1] = '\0';
2109     free(names.namelist_val);
2110     rc = 1;
2111
2112   fail_pts_UserGet:
2113
2114     if (st != NULL) {
2115         *st = tst;
2116     }
2117     return rc;
2118 }
2119
2120 /*
2121  * pts_UserRename - rename a user.
2122  *
2123  * PARAMETERS
2124  *
2125  * IN cellHandle - a previously opened cellHandle that corresponds
2126  * to the cell where the group exists.
2127  *
2128  * IN oldName - the name of the user to rename.
2129  *
2130  * IN newName - the new user name.
2131  *
2132  * LOCKS
2133  *
2134  * No locks are held by this function
2135  *
2136  * RETURN CODES
2137  *
2138  * Returns != 0 upon successful completion.
2139  *
2140  */
2141
2142 int ADMINAPI
2143 pts_UserRename(const void *cellHandle, const char *oldName,
2144                char *newName, afs_status_p st)
2145 {
2146     int rc = 0;
2147     afs_status_t tst = 0;
2148     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2149     afs_int32 userId = 0;
2150
2151     /*
2152      * Validate arguments
2153      */
2154
2155     if (!IsValidCellHandle(c_handle, &tst)) {
2156         goto fail_pts_UserRename;
2157     }
2158
2159     if ((oldName == NULL) || (*oldName == 0)) {
2160         tst = ADMPTSOLDNAMENULL;
2161         goto fail_pts_UserRename;
2162     }
2163
2164     if ((newName == NULL) || (*newName == 0)) {
2165         tst = ADMPTSNEWNAMENULL;
2166         goto fail_pts_UserRename;
2167     }
2168
2169     /*
2170      * Translate the user name into an id.
2171      */
2172
2173     if (!TranslateOneName
2174         (c_handle, oldName, ADMPTSOLDNAMETOOLONG, &userId, &tst)) {
2175         goto fail_pts_UserRename;
2176     }
2177
2178     /*
2179      * Make the rpc
2180      */
2181
2182     tst = ubik_PR_ChangeEntry(c_handle->pts, 0, userId, newName, 0, 0);
2183
2184     if (tst != 0) {
2185         goto fail_pts_UserRename;
2186     }
2187     rc = 1;
2188
2189   fail_pts_UserRename:
2190
2191     if (st != NULL) {
2192         *st = tst;
2193     }
2194     return rc;
2195 }
2196
2197 /*
2198  * SetUserAccess - translate our Access notation to pts flags.
2199  *
2200  * PARAMETERS
2201  *
2202  * IN userP - the user structure that contains the new permissions.
2203  *
2204  * OUT flags - a pointer to an afs_int32 structure that 
2205  * contains the flags to pass to pts.
2206  *
2207  * LOCKS
2208  *
2209  * No locks are held by this function
2210  *
2211  * RETURN CODES
2212  *
2213  * Returns != 0 upon successful completion.
2214  *
2215  */
2216
2217 static int
2218 SetUserAccess(const pts_UserUpdateEntry_p userP, afs_int32 * flags,
2219               afs_status_p st)
2220 {
2221     int rc = 0;
2222     afs_status_t tst = 0;
2223
2224     *flags = 0;
2225
2226     if (userP->listMembership == PTS_USER_ANYUSER_ACCESS) {
2227         *flags |= 16;
2228     }
2229
2230     if (userP->listGroupsOwned == PTS_USER_ANYUSER_ACCESS) {
2231         *flags |= 32;
2232     }
2233
2234     if (userP->listStatus == PTS_USER_ANYUSER_ACCESS) {
2235         *flags |= 128;
2236     }
2237     rc = 1;
2238
2239     if (st != NULL) {
2240         *st = tst;
2241     }
2242     return rc;
2243 }
2244
2245
2246 /*
2247  * pts_UserModify - update a user entry.
2248  *
2249  * PARAMETERS
2250  *
2251  * IN cellHandle - a previously opened cellHandle that corresponds
2252  * to the cell where the group exists.
2253  *
2254  * IN userName - the name of the user to update.
2255  *
2256  * IN newEntryP - a pointer to a pts_UserUpdateEntry_t that contains the
2257  * new information for user.
2258  *
2259  * LOCKS
2260  *
2261  * No locks are held by this function
2262  *
2263  * RETURN CODES
2264  *
2265  * Returns != 0 upon successful completion.
2266  *
2267  */
2268
2269 int ADMINAPI
2270 pts_UserModify(const void *cellHandle, const char *userName,
2271                const pts_UserUpdateEntry_p newEntryP, afs_status_p st)
2272 {
2273     int rc = 0;
2274     afs_status_t tst = 0;
2275     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2276     afs_int32 userId = 0;
2277     afs_int32 newQuota = 0;
2278     afs_int32 mask = 0;
2279     afs_int32 flags = 0;
2280
2281     /*
2282      * Validate arguments
2283      */
2284
2285     if (!IsValidCellHandle(c_handle, &tst)) {
2286         goto fail_pts_UserModify;
2287     }
2288
2289     if ((userName == NULL) || (*userName == 0)) {
2290         tst = ADMPTSUSERNAMENULL;
2291         goto fail_pts_UserModify;
2292     }
2293
2294     if (newEntryP == NULL) {
2295         tst = ADMPTSNEWENTRYPNULL;
2296         goto fail_pts_UserModify;
2297     }
2298
2299     /*
2300      * Translate the user name into an id.
2301      */
2302
2303     if (!TranslateOneName
2304         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &userId, &tst)) {
2305         goto fail_pts_UserModify;
2306     }
2307
2308
2309     if (newEntryP->flag & PTS_USER_UPDATE_GROUP_CREATE_QUOTA) {
2310         mask |= PR_SF_NGROUPS;
2311         newQuota = newEntryP->groupCreationQuota;
2312     }
2313
2314     if (newEntryP->flag & PTS_USER_UPDATE_PERMISSIONS) {
2315         mask |= PR_SF_ALLBITS;
2316         if (!SetUserAccess(newEntryP, &flags, &tst)) {
2317             goto fail_pts_UserModify;
2318         }
2319     }
2320
2321     /*
2322      * Make the rpc
2323      */
2324
2325     tst =
2326         ubik_PR_SetFieldsEntry(c_handle->pts, 0, userId, mask, flags,
2327                   newQuota, 0, 0, 0);
2328
2329     if (tst != 0) {
2330         goto fail_pts_UserModify;
2331     }
2332     rc = 1;
2333
2334   fail_pts_UserModify:
2335
2336     if (st != NULL) {
2337         *st = tst;
2338     }
2339     return rc;
2340 }
2341
2342 /*
2343  * pts_UserMaxGet - get the maximum in use user id.
2344  *
2345  * PARAMETERS
2346  *
2347  * IN cellHandle - a previously opened cellHandle that corresponds
2348  * to the cell where the group exists.
2349  *
2350  * OUT maxUserId - upon successful completion contains the max in use id.
2351  *
2352  * LOCKS
2353  *
2354  * No locks are held by this function
2355  *
2356  * RETURN CODES
2357  *
2358  * Returns != 0 upon successful completion.
2359  *
2360  */
2361
2362 int ADMINAPI
2363 pts_UserMaxGet(const void *cellHandle, int *maxUserId, afs_status_p st)
2364 {
2365     int rc = 0;
2366     afs_status_t tst = 0;
2367     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2368     afs_int32 maxGroupId = 0;
2369
2370     /*
2371      * Validate arguments
2372      */
2373
2374     if (!IsValidCellHandle(c_handle, &tst)) {
2375         goto fail_pts_UserMaxGet;
2376     }
2377
2378     if (maxUserId == NULL) {
2379         tst = ADMPTSMAXUSERIDNULL;
2380         goto fail_pts_UserMaxGet;
2381     }
2382
2383     tst = ubik_PR_ListMax(c_handle->pts, 0, maxUserId, &maxGroupId);
2384
2385     if (tst != 0) {
2386         goto fail_pts_UserMaxGet;
2387     }
2388     rc = 1;
2389
2390   fail_pts_UserMaxGet:
2391
2392     if (st != NULL) {
2393         *st = tst;
2394     }
2395     return rc;
2396 }
2397
2398 /*
2399  * pts_UserMaxSet - set the maximum user id.
2400  *
2401  * PARAMETERS
2402  *
2403  * IN cellHandle - a previously opened cellHandle that corresponds
2404  * to the cell where the group exists.
2405  *
2406  * IN maxUserId - the new max user id.
2407  *
2408  * LOCKS
2409  *
2410  * No locks are held by this function
2411  *
2412  * RETURN CODES
2413  *
2414  * Returns != 0 upon successful completion.
2415  *
2416  */
2417
2418 int ADMINAPI
2419 pts_UserMaxSet(const void *cellHandle, int maxUserId, afs_status_p st)
2420 {
2421     int rc = 0;
2422     afs_status_t tst = 0;
2423     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2424
2425     /*
2426      * Validate arguments
2427      */
2428
2429     if (!IsValidCellHandle(c_handle, &tst)) {
2430         goto fail_pts_UserMaxSet;
2431     }
2432
2433     tst = ubik_PR_SetMax(c_handle->pts, 0, maxUserId, 0);
2434
2435     if (tst != 0) {
2436         goto fail_pts_UserMaxSet;
2437     }
2438     rc = 1;
2439
2440   fail_pts_UserMaxSet:
2441
2442     if (st != NULL) {
2443         *st = tst;
2444     }
2445     return rc;
2446 }
2447
2448 /*
2449  * pts_UserMemberListBegin - begin iterating over the list of groups
2450  * a particular user belongs to.
2451  *
2452  * PARAMETERS
2453  *
2454  * IN cellHandle - a previously opened cellHandle that corresponds
2455  * to the cell where the group exists.
2456  *
2457  * IN groupName - the group whose members will be returned.
2458  *
2459  * OUT iterationIdP - upon successful completion contains a iterator that
2460  * can be passed to pts_GroupMemberListNext.
2461  *
2462  * LOCKS
2463  *
2464  * No locks are obtained or released by this function
2465  *
2466  * RETURN CODES
2467  *
2468  * Returns != 0 upon successful completion.
2469  *
2470  */
2471
2472 int ADMINAPI
2473 pts_UserMemberListBegin(const void *cellHandle, const char *userName,
2474                         void **iterationIdP, afs_status_p st)
2475 {
2476     return MemberListBegin(cellHandle, userName, ADMPTSUSERNAMENULL,
2477                            ADMPTSUSERNAMETOOLONG, iterationIdP, st);
2478
2479 }
2480
2481 /*
2482  * pts_UserMemberListNext - get the next group a user belongs to
2483  *
2484  * PARAMETERS
2485  *
2486  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2487  *
2488  * OUT userName - upon successful completion contains the next group a user
2489  * belongs to.
2490  *
2491  * LOCKS
2492  *
2493  * The iterator mutex is held during the retrieval of the next member.
2494  *
2495  * RETURN CODES
2496  *
2497  * Returns != 0 upon successful completion.
2498  *
2499  */
2500
2501 int ADMINAPI
2502 pts_UserMemberListNext(const void *iterationId, char *userName,
2503                        afs_status_p st)
2504 {
2505     return pts_GroupMemberListNext(iterationId, userName, st);
2506 }
2507
2508 /*
2509  * pts_UserMemberListDone - finish using a user list iterator
2510  *
2511  * PARAMETERS
2512  *
2513  * IN iterationId - an iterator previously returned by pts_UserMemberListBegin
2514  *
2515  * LOCKS
2516  *
2517  * The iterator is locked and then destroyed
2518  *
2519  * RETURN CODES
2520  *
2521  * Returns != 0 upon successful completion.
2522  *
2523  * ASSUMPTIONS
2524  *
2525  * It is the user's responsibility to make sure pts_UserMemberListDone
2526  * is called only once for each iterator.
2527  */
2528
2529 int ADMINAPI
2530 pts_UserMemberListDone(const void *iterationId, afs_status_p st)
2531 {
2532     return pts_GroupMemberListDone(iterationId, st);
2533 }
2534
2535 typedef struct owned_group_list {
2536     namelist owned_names;       /* the list of character names owned by this id */
2537     prlist owned_ids;           /* the list of pts ids owned by this id */
2538     afs_int32 index;            /* the index into owned_names for the next group */
2539     afs_int32 owner;            /* the pts id of the owner */
2540     afs_int32 more;             /* the last parameter to PR_ListOwned */
2541     int finished_retrieving;    /* set when we've processed the last owned_names */
2542     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2543     char group[CACHED_ITEMS][PTS_MAX_NAME_LEN]; /* cache of names */
2544 } owned_group_list_t, *owned_group_list_p;
2545
2546 static int
2547 DeleteOwnedGroupSpecificData(void *rpc_specific, afs_status_p st)
2548 {
2549     int rc = 0;
2550     afs_status_t tst = 0;
2551     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2552
2553     if (list->owned_names.namelist_val != NULL) {
2554         free(list->owned_names.namelist_val);
2555     }
2556
2557     if (list->owned_ids.prlist_val != NULL) {
2558         free(list->owned_ids.prlist_val);
2559     }
2560     rc = 1;
2561
2562     if (st != NULL) {
2563         *st = tst;
2564     }
2565     return rc;
2566 }
2567
2568 static int
2569 GetOwnedGroupRPC(void *rpc_specific, int slot, int *last_item,
2570                  int *last_item_contains_data, afs_status_p st)
2571 {
2572     int rc = 0;
2573     afs_status_t tst = 0;
2574     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2575
2576     /*
2577      * We really don't make an rpc for every entry we return here
2578      * since the pts interface allows several members to be returned
2579      * with one rpc, but we fake it to make the iterator happy.
2580      */
2581
2582     /*
2583      * Check to see if we are done retrieving data
2584      */
2585
2586     if ((list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2587         *last_item = 1;
2588         *last_item_contains_data = 0;
2589         goto fail_GetOwnedGroupRPC;
2590     }
2591
2592     /*
2593      * Check to see if we really need to make an rpc
2594      */
2595
2596     if ((!list->finished_retrieving) && (list->owned_names.namelist_len == 0)) {
2597         tst =
2598             ubik_PR_ListOwned(list->c_handle->pts, 0, list->owner,
2599                       &list->owned_ids, &list->more);
2600         if (tst != 0) {
2601             goto fail_GetOwnedGroupRPC;
2602         }
2603
2604         if (!TranslatePTSIds
2605             (list->c_handle, &list->owned_names, (idlist *) & list->owned_ids,
2606              &tst)) {
2607             goto fail_GetOwnedGroupRPC;
2608         }
2609         list->index = 0;
2610
2611         if (list->owned_names.namelist_val == NULL) {
2612             *last_item = 1;
2613             *last_item_contains_data = 0;
2614             goto fail_GetOwnedGroupRPC;
2615         }
2616     }
2617
2618     /*
2619      * We can retrieve the next group from data we already received
2620      */
2621
2622     strcpy(list->group[slot], list->owned_names.namelist_val[list->index]);
2623     list->index++;
2624
2625     /*
2626      * Check to see if there is more data to be retrieved
2627      * We need to free up the previously retrieved data here
2628      * and then check to see if the last rpc indicated that there
2629      * were more items to retrieve.
2630      */
2631
2632     if (list->index >= list->owned_names.namelist_len) {
2633         list->owned_names.namelist_len = 0;
2634         free(list->owned_names.namelist_val);
2635         list->owned_names.namelist_val = 0;
2636
2637         list->owned_ids.prlist_len = 0;
2638         free(list->owned_ids.prlist_val);
2639         list->owned_ids.prlist_val = 0;
2640
2641         if (!list->more) {
2642             list->finished_retrieving = 1;
2643         }
2644     }
2645     rc = 1;
2646
2647   fail_GetOwnedGroupRPC:
2648
2649     if (st != NULL) {
2650         *st = tst;
2651     }
2652     return rc;
2653 }
2654
2655 static int
2656 GetOwnedGroupFromCache(void *rpc_specific, int slot, void *dest,
2657                        afs_status_p st)
2658 {
2659     int rc = 0;
2660     afs_status_t tst = 0;
2661     owned_group_list_p list = (owned_group_list_p) rpc_specific;
2662
2663     strcpy((char *)dest, list->group[slot]);
2664     rc = 1;
2665
2666     if (st != NULL) {
2667         *st = tst;
2668     }
2669
2670     return rc;
2671 }
2672
2673 /*
2674  * pts_OwnedGroupListBegin - begin iterating over the list of groups
2675  * a particular user owns.
2676  *
2677  * PARAMETERS
2678  *
2679  * IN cellHandle - a previously opened cellHandle that corresponds
2680  * to the cell where the group exists.
2681  *
2682  * IN ownerName - the owner of the groups of interest.
2683  *
2684  * OUT iterationIdP - upon successful completion contains a iterator that
2685  * can be passed to pts_OwnedGroupListNext.
2686  *
2687  * LOCKS
2688  *
2689  * No locks are held by this function
2690  *
2691  * RETURN CODES
2692  *
2693  * Returns != 0 upon successful completion.
2694  *
2695  */
2696
2697 int ADMINAPI
2698 pts_OwnedGroupListBegin(const void *cellHandle, const char *userName,
2699                         void **iterationIdP, afs_status_p st)
2700 {
2701     int rc = 0;
2702     afs_status_t tst = 0;
2703     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
2704     afs_admin_iterator_p iter =
2705         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
2706     owned_group_list_p list =
2707         (owned_group_list_p) malloc(sizeof(owned_group_list_t));
2708
2709     /*
2710      * Validate arguments
2711      */
2712
2713     if (!IsValidCellHandle(c_handle, &tst)) {
2714         goto fail_pts_OwnedGroupListBegin;
2715     }
2716
2717     if ((userName == NULL) || (*userName == 0)) {
2718         tst = ADMPTSUSERNAMENULL;
2719         goto fail_pts_OwnedGroupListBegin;
2720     }
2721
2722     if (iterationIdP == NULL) {
2723         tst = ADMITERATORNULL;
2724         goto fail_pts_OwnedGroupListBegin;
2725     }
2726
2727     if ((iter == NULL) || (list == NULL)) {
2728         tst = ADMNOMEM;
2729         goto fail_pts_OwnedGroupListBegin;
2730     }
2731
2732     /*
2733      * Initialize the iterator specific data
2734      */
2735
2736     list->index = 0;
2737     list->finished_retrieving = 0;
2738     list->c_handle = c_handle;
2739     list->owned_names.namelist_len = 0;
2740     list->owned_names.namelist_val = 0;
2741     list->owned_ids.prlist_len = 0;
2742     list->owned_ids.prlist_val = 0;
2743
2744     /*
2745      * Translate the user name into an id.
2746      */
2747
2748     if (!TranslateOneName
2749         (c_handle, userName, ADMPTSUSERNAMETOOLONG, &list->owner, &tst)) {
2750         goto fail_pts_OwnedGroupListBegin;
2751     }
2752
2753     if (IteratorInit
2754         (iter, (void *)list, GetOwnedGroupRPC, GetOwnedGroupFromCache, NULL,
2755          DeleteOwnedGroupSpecificData, &tst)) {
2756         *iterationIdP = (void *)iter;
2757         rc = 1;
2758     }
2759
2760   fail_pts_OwnedGroupListBegin:
2761
2762     if (rc == 0) {
2763         if (iter != NULL) {
2764             free(iter);
2765         }
2766         if (list != NULL) {
2767             free(list);
2768         }
2769     }
2770
2771     if (st != NULL) {
2772         *st = tst;
2773     }
2774     return rc;
2775 }
2776
2777 /*
2778  * pts_OwnedGroupListNext - get the next group a user owns.
2779  *
2780  * PARAMETERS
2781  *
2782  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2783  *
2784  * OUT groupName - upon successful completion contains the next group a user
2785  * owns.
2786  *
2787  * LOCKS
2788  *
2789  * The iterator mutex is held during the retrieval of the next member.
2790  *
2791  * RETURN CODES
2792  *
2793  * Returns != 0 upon successful completion.
2794  *
2795  */
2796
2797 int ADMINAPI
2798 pts_OwnedGroupListNext(const void *iterationId, char *groupName,
2799                        afs_status_p st)
2800 {
2801     int rc = 0;
2802     afs_status_t tst = 0;
2803     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2804
2805     /*
2806      * Validate arguments
2807      */
2808
2809     if (iterationId == NULL) {
2810         tst = ADMITERATORNULL;
2811         goto fail_pts_OwnedGroupListNext;
2812     }
2813
2814     if (groupName == NULL) {
2815         tst = ADMPTSGROUPNAMENULL;
2816         goto fail_pts_OwnedGroupListNext;
2817     }
2818
2819     rc = IteratorNext(iter, (void *)groupName, &tst);
2820
2821   fail_pts_OwnedGroupListNext:
2822
2823     if (st != NULL) {
2824         *st = tst;
2825     }
2826     return rc;
2827 }
2828
2829 /*
2830  * pts_OwnedGroupListDone - finish using a group list iterator
2831  *
2832  * PARAMETERS
2833  *
2834  * IN iterationId - an iterator previously returned by pts_OwnedGroupListBegin
2835  *
2836  * LOCKS
2837  *
2838  * The iterator is locked and then destroyed
2839  *
2840  * RETURN CODES
2841  *
2842  * Returns != 0 upon successful completion.
2843  *
2844  * ASSUMPTIONS
2845  *
2846  * It is the user's responsibility to make sure pts_OwnedGroupListDone
2847  * is called only once for each iterator.
2848  */
2849
2850 int ADMINAPI
2851 pts_OwnedGroupListDone(const void *iterationId, afs_status_p st)
2852 {
2853     int rc = 0;
2854     afs_status_t tst = 0;
2855     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
2856
2857     /*
2858      * Validate arguments
2859      */
2860
2861     if (iterationId == NULL) {
2862         tst = ADMITERATORNULL;
2863         goto fail_pts_OwnedGroupListDone;
2864     }
2865
2866     rc = IteratorDone(iter, &tst);
2867
2868   fail_pts_OwnedGroupListDone:
2869
2870     if (st != NULL) {
2871         *st = tst;
2872     }
2873     return rc;
2874 }
2875
2876 typedef struct pts_list {
2877     prlistentries *names;       /* the current list of pts names in this cell */
2878     prlistentries *currName;    /* the current pts entry */
2879     afs_int32 index;            /* the index into names for the next pts entry */
2880     afs_int32 nextstartindex;   /* the next start index for the RPC */
2881     afs_int32 nentries;         /* the number of entries in names */
2882     afs_int32 flag;             /* the type of the list */
2883     int finished_retrieving;    /* set when we've processed the last owned_names */
2884     afs_cell_handle_p c_handle; /* ubik client to pts server's from c_handle */
2885     char entries[CACHED_ITEMS][PTS_MAX_NAME_LEN];       /* cache of pts names */
2886 } pts_list_t, *pts_list_p;
2887
2888 static int
2889 DeletePTSSpecificData(void *rpc_specific, afs_status_p st)
2890 {
2891     int rc = 0;
2892     afs_status_t tst = 0;
2893     pts_list_p list = (pts_list_p) rpc_specific;
2894
2895     if (list->names) {
2896         free(list->names);
2897     }
2898
2899     rc = 1;
2900
2901     if (st != NULL) {
2902         *st = tst;
2903     }
2904     return rc;
2905 }
2906
2907 static int
2908 GetPTSRPC(void *rpc_specific, int slot, int *last_item,
2909           int *last_item_contains_data, afs_status_p st)
2910 {
2911     int rc = 0;
2912     afs_status_t tst = 0;
2913     pts_list_p list = (pts_list_p) rpc_specific;
2914
2915     /*
2916      * We really don't make an rpc for every entry we return here
2917      * since the pts interface allows several members to be returned
2918      * with one rpc, but we fake it to make the iterator happy.
2919      */
2920
2921     /*
2922      * Check to see if we are done retrieving data
2923      */
2924
2925     if (list->finished_retrieving) {
2926         *last_item = 1;
2927         *last_item_contains_data = 0;
2928         goto fail_GetPTSRPC;
2929     }
2930
2931     /*
2932      * Check to see if we really need to make an rpc
2933      */
2934
2935     if ((!list->finished_retrieving) && (list->index >= list->nentries)) {
2936         afs_int32 start = list->nextstartindex;
2937         prentries bulkentries;
2938         list->nextstartindex = -1;
2939         bulkentries.prentries_val = 0;
2940         bulkentries.prentries_len = 0;
2941
2942         tst =
2943             ubik_PR_ListEntries(list->c_handle->pts, 0, list->flag,
2944                       start, &bulkentries, &(list->nextstartindex));
2945
2946         if (tst != 0) {
2947             goto fail_GetPTSRPC;
2948         }
2949
2950         list->nentries = bulkentries.prentries_len;
2951         list->names = bulkentries.prentries_val;
2952
2953         list->index = 0;
2954         list->currName = list->names;
2955
2956     }
2957
2958     /*
2959      * We can retrieve the next entry from data we already received
2960      */
2961
2962     strcpy(list->entries[slot], list->currName->name);
2963     list->index++;
2964     list->currName++;
2965
2966
2967     /*
2968      * Check to see if there is more data to be retrieved
2969      * We need to free up the previously retrieved data here
2970      * and then check to see if the last rpc indicated that there
2971      * were more items to retrieve.
2972      */
2973
2974     if (list->index >= list->nentries) {
2975         if (list->names) {
2976             free(list->names);
2977         }
2978         list->names = NULL;
2979
2980         if (list->nextstartindex == -1) {
2981             list->finished_retrieving = 1;
2982         }
2983     }
2984     rc = 1;
2985
2986   fail_GetPTSRPC:
2987
2988     if (st != NULL) {
2989         *st = tst;
2990     }
2991
2992     return rc;
2993 }
2994
2995 static int
2996 GetPTSFromCache(void *rpc_specific, int slot, void *dest, afs_status_p st)
2997 {
2998     int rc = 0;
2999     afs_status_t tst = 0;
3000     pts_list_p list = (pts_list_p) rpc_specific;
3001
3002     strcpy((char *)dest, list->entries[slot]);
3003     rc = 1;
3004
3005     if (st != NULL) {
3006         *st = tst;
3007     }
3008
3009     return rc;
3010 }
3011
3012 /*
3013  * pts_UserListBegin - begin iterating over the list of users
3014  * in a particular cell
3015  *
3016  * PARAMETERS
3017  *
3018  * IN cellHandle - a previously opened cellHandle that corresponds
3019  * to the cell where the users exist.
3020  *
3021  * OUT iterationIdP - upon successful completion contains a iterator that
3022  * can be passed to pts_UserListNext.
3023  *
3024  * LOCKS
3025  *
3026  * No locks are held by this function
3027  *
3028  * RETURN CODES
3029  *
3030  * Returns != 0 upon successful completion.
3031  *
3032  */
3033
3034 int ADMINAPI
3035 pts_UserListBegin(const void *cellHandle, void **iterationIdP,
3036                   afs_status_p st)
3037 {
3038     int rc = 0;
3039     afs_status_t tst = 0;
3040     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3041     afs_admin_iterator_p iter =
3042         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3043     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3044
3045     /*
3046      * Validate arguments
3047      */
3048
3049     if (!IsValidCellHandle(c_handle, &tst)) {
3050         goto fail_pts_UserListBegin;
3051     }
3052
3053     if (iterationIdP == NULL) {
3054         tst = ADMITERATORNULL;
3055         goto fail_pts_UserListBegin;
3056     }
3057
3058     if ((iter == NULL) || (list == NULL)) {
3059         tst = ADMNOMEM;
3060         goto fail_pts_UserListBegin;
3061     }
3062
3063     /*
3064      * Initialize the iterator specific data
3065      */
3066
3067     list->index = 0;
3068     list->finished_retrieving = 0;
3069     list->c_handle = c_handle;
3070     list->names = NULL;
3071     list->nextstartindex = 0;
3072     list->nentries = 0;
3073     list->flag = PRUSERS;
3074
3075     if (IteratorInit
3076         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3077          DeletePTSSpecificData, &tst)) {
3078         *iterationIdP = (void *)iter;
3079         rc = 1;
3080     }
3081
3082   fail_pts_UserListBegin:
3083
3084     if (rc == 0) {
3085         if (iter != NULL) {
3086             free(iter);
3087         }
3088         if (list != NULL) {
3089             free(list);
3090         }
3091     }
3092
3093     if (st != NULL) {
3094         *st = tst;
3095     }
3096     return rc;
3097 }
3098
3099 /*
3100  * pts_UserListNext - get the next user in the cell.
3101  *
3102  * PARAMETERS
3103  *
3104  * IN iterationId - an iterator previously returned by pts_UserListBegin
3105  *
3106  * OUT groupName - upon successful completion contains the next user
3107  *
3108  * LOCKS
3109  *
3110  * The iterator mutex is held during the retrieval of the next member.
3111  *
3112  * RETURN CODES
3113  *
3114  * Returns != 0 upon successful completion.
3115  *
3116  */
3117
3118 int ADMINAPI
3119 pts_UserListNext(const void *iterationId, char *userName, afs_status_p st)
3120 {
3121     int rc = 0;
3122     afs_status_t tst = 0;
3123     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3124
3125     /*
3126      * Validate arguments
3127      */
3128
3129     if (iterationId == NULL) {
3130         tst = ADMITERATORNULL;
3131         goto fail_pts_UserListNext;
3132     }
3133
3134     if (userName == NULL) {
3135         tst = ADMPTSUSERNAMENULL;
3136         goto fail_pts_UserListNext;
3137     }
3138
3139     rc = IteratorNext(iter, (void *)userName, &tst);
3140
3141   fail_pts_UserListNext:
3142
3143     if (st != NULL) {
3144         *st = tst;
3145     }
3146     return rc;
3147 }
3148
3149 /*
3150  * pts_UserListDone - finish using a user list iterator
3151  *
3152  * PARAMETERS
3153  *
3154  * IN iterationId - an iterator previously returned by pts_UserListBegin
3155  *
3156  * LOCKS
3157  *
3158  * The iterator is locked and then destroyed
3159  *
3160  * RETURN CODES
3161  *
3162  * Returns != 0 upon successful completion.
3163  *
3164  * ASSUMPTIONS
3165  *
3166  * It is the user's responsibility to make sure pts_UserListDone
3167  * is called only once for each iterator.
3168  */
3169
3170 int ADMINAPI
3171 pts_UserListDone(const void *iterationId, afs_status_p st)
3172 {
3173     int rc = 0;
3174     afs_status_t tst = 0;
3175     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3176
3177     /*
3178      * Validate arguments
3179      */
3180
3181     if (iterationId == NULL) {
3182         tst = ADMITERATORNULL;
3183         goto fail_pts_UserListDone;
3184     }
3185
3186     rc = IteratorDone(iter, &tst);
3187
3188   fail_pts_UserListDone:
3189
3190     if (st != NULL) {
3191         *st = tst;
3192     }
3193     return rc;
3194 }
3195
3196 /*
3197  * pts_GroupListBegin - begin iterating over the list of groups
3198  * in a particular cell.
3199  *
3200  * PARAMETERS
3201  *
3202  * IN cellHandle - a previously opened cellHandle that corresponds
3203  * to the cell where the groups exist.
3204  *
3205  * OUT iterationIdP - upon successful completion contains a iterator that
3206  * can be passed to pts_GroupListNext.
3207  *
3208  * LOCKS
3209  *
3210  * No locks are held by this function
3211  *
3212  * RETURN CODES
3213  *
3214  * Returns != 0 upon successful completion.
3215  *
3216  */
3217
3218 int ADMINAPI
3219 pts_GroupListBegin(const void *cellHandle, void **iterationIdP,
3220                    afs_status_p st)
3221 {
3222     int rc = 0;
3223     afs_status_t tst = 0;
3224     afs_cell_handle_p c_handle = (afs_cell_handle_p) cellHandle;
3225     afs_admin_iterator_p iter =
3226         (afs_admin_iterator_p) malloc(sizeof(afs_admin_iterator_t));
3227     pts_list_p list = (pts_list_p) malloc(sizeof(pts_list_t));
3228
3229     /*
3230      * Validate arguments
3231      */
3232
3233     if (!IsValidCellHandle(c_handle, &tst)) {
3234         goto fail_pts_GroupListBegin;
3235     }
3236
3237     if (iterationIdP == NULL) {
3238         tst = ADMITERATORNULL;
3239         goto fail_pts_GroupListBegin;
3240     }
3241
3242     if ((iter == NULL) || (list == NULL)) {
3243         tst = ADMNOMEM;
3244         goto fail_pts_GroupListBegin;
3245     }
3246
3247     /*
3248      * Initialize the iterator specific data
3249      */
3250
3251     list->index = 0;
3252     list->finished_retrieving = 0;
3253     list->c_handle = c_handle;
3254     list->names = NULL;
3255     list->nextstartindex = 0;
3256     list->nentries = 0;
3257     list->flag = PRGROUPS;
3258
3259     if (IteratorInit
3260         (iter, (void *)list, GetPTSRPC, GetPTSFromCache, NULL,
3261          DeletePTSSpecificData, &tst)) {
3262         *iterationIdP = (void *)iter;
3263         rc = 1;
3264     }
3265
3266   fail_pts_GroupListBegin:
3267
3268     if (rc == 0) {
3269         if (iter != NULL) {
3270             free(iter);
3271         }
3272         if (list != NULL) {
3273             free(list);
3274         }
3275     }
3276
3277     if (st != NULL) {
3278         *st = tst;
3279     }
3280     return rc;
3281 }
3282
3283 /*
3284  * pts_UserListNext - get the next group in a cell.
3285  *
3286  * PARAMETERS
3287  *
3288  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3289  *
3290  * OUT groupName - upon successful completion contains the next group
3291  *
3292  * LOCKS
3293  *
3294  * The iterator mutex is held during the retrieval of the next member.
3295  *
3296  * RETURN CODES
3297  *
3298  * Returns != 0 upon successful completion.
3299  *
3300  */
3301
3302 int ADMINAPI
3303 pts_GroupListNext(const void *iterationId, char *groupName, afs_status_p st)
3304 {
3305     int rc = 0;
3306     afs_status_t tst = 0;
3307     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3308
3309     /*
3310      * Validate arguments
3311      */
3312
3313     if (iterationId == NULL) {
3314         tst = ADMITERATORNULL;
3315         goto fail_pts_GroupListNext;
3316     }
3317
3318     if (groupName == NULL) {
3319         tst = ADMPTSGROUPNAMENULL;
3320         goto fail_pts_GroupListNext;
3321     }
3322
3323     rc = IteratorNext(iter, (void *)groupName, &tst);
3324
3325   fail_pts_GroupListNext:
3326
3327     if (st != NULL) {
3328         *st = tst;
3329     }
3330     return rc;
3331 }
3332
3333 /*
3334  * pts_GroupListDone - finish using a group list iterator
3335  *
3336  * PARAMETERS
3337  *
3338  * IN iterationId - an iterator previously returned by pts_GroupListBegin
3339  *
3340  * LOCKS
3341  *
3342  * The iterator is locked and then destroyed
3343  *
3344  * RETURN CODES
3345  *
3346  * Returns != 0 upon successful completion.
3347  *
3348  * ASSUMPTIONS
3349  *
3350  * It is the user's responsibility to make sure pts_GroupListDone
3351  * is called only once for each iterator.
3352  */
3353
3354 int ADMINAPI
3355 pts_GroupListDone(const void *iterationId, afs_status_p st)
3356 {
3357     int rc = 0;
3358     afs_status_t tst = 0;
3359     afs_admin_iterator_p iter = (afs_admin_iterator_p) iterationId;
3360
3361     /*
3362      * Validate arguments
3363      */
3364
3365     if (iterationId == NULL) {
3366         tst = ADMITERATORNULL;
3367         goto fail_pts_GroupListDone;
3368     }
3369
3370     rc = IteratorDone(iter, &tst);
3371
3372   fail_pts_GroupListDone:
3373
3374     if (st != NULL) {
3375         *st = tst;
3376     }
3377     return rc;
3378 }