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