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