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