1 ################################################################################
4 # Description: Routines for generating portions of CVSAdm
5 # Author: Andrew@DeFaria.com
6 # Created: Fri Jul 8 12:35:48 PDT 2005
10 # (c) Copyright 2005, LynuxWorks Inc., all rights reserved.
12 ################################################################################
16 use CGI qw (:standard *table start_Tr end_Tr start_div end_div);
17 use Fcntl ':flock'; # import LOCK_* constants
18 use vars qw (@ISA @EXPORT);
51 # CVSAdm web app runs from a web server therefore it's running as an
52 # unprivileged user (usually the user apache) yet we want to maintain
53 # CVS user/group/sysuser information on CVS servers. As such we need a
54 # list of CVS servers to adminster. The $cvsadm_conf file describes the
55 # servers and repositories that we are allowed to manage. We will then
56 # rely on CVS itself to checkout the CVSROOT directory, modify the files
57 # appropriately then use CVS to commit these changes.
58 my $cvsadm_conf = "cvsadm.conf";
60 # These are the lists of special files that cvsadm managed under CVSROOT
110 my $cvs_server = shift;
111 my $repository = shift;
115 my $filename = $file;
116 $file = "$cvs_server/$repository/CVSROOT/$file";
122 my @lines = Read $file;
129 $found = 1 if $line eq $userid;
132 push @lines, $userid . "\n" if !$found;
134 my $euid = cookie "CVSAdmUser";
138 "$euid added $userid";
140 CVSCommit $cvs_server, $repository, $filename, $commit_msg, sort (@lines);
146 my $cvs_server = shift;
147 my $repository = shift;
150 my $groups = "$cvs_server/$repository/CVSROOT/groups";
154 my @groups = Read $groups;
159 return 1, "Group $group already exists" if $group eq $line;
162 push @groups, $group . "\n";
164 my $euid = cookie "CVSAdmUser";
167 "Added group $group" :
168 "$euid added group $group";
170 CVSCommit $cvs_server, $repository, "groups", $commit_msg, sort (@groups);
174 return 0, "Added group $group";
178 my $cvs_server = shift;
179 my $repository = shift;
182 my $sysusers = "$cvs_server/$repository/CVSROOT/sysusers";
186 my @sysusers = Read $sysusers;
188 foreach (@sysusers) {
191 return 1, "Sysuser $sysuser already exists" if $sysuser eq $line;
194 push @sysusers, $sysuser . "\n";
196 CVSCommit $cvs_server, $repository, "sysusers", "Added sysuser $sysuser", sort (@sysusers);
200 return 0, "Added sysuser $sysuser";
204 my $cvs_server = shift;
205 my $repository = shift;
206 my %user_record = @_;
208 # Check if userid already exists
209 my %passwd = OpenPasswd $cvs_server, $repository;
213 $user_record {userid} .
215 if $passwd{$user_record {userid}};
217 # Format passwd entry
219 $fields {password} = crypt $user_record {password}, "xx";
220 $fields {system_user} = $user_record {system_user};
221 $fields {fullname} = $user_record {fullname};
222 $fields {email} = $user_record {email};
224 # Handle groups (comma separated)
225 my @groups = split /,/, $user_record {groups};
226 $fields {groups} = \@groups;
228 $passwd {$user_record {userid}} = \%fields;
230 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
236 foreach (sort (keys %passwd)) {
237 my %fields = %{$passwd {$_}};
242 foreach (@{$fields {groups}}) {
253 $fields {password} . ":" .
254 $fields {system_user} . ":" .
255 $fields {fullname} . ":" .
256 $fields {email} . ":" .
259 push @passwd, $passwd_line;
262 my $euid = cookie "CVSAdmUser";
265 "Added user " . $user_record {userid} :
266 "$euid added" . $user_record {userid};
268 CVSCommit $cvs_server, $repository, "passwd", $commit_msg, sort (@passwd);
272 # Update readers and writers
273 UpdateAccess $cvs_server, $repository, $user_record {userid}, $user_record {$repository};
275 return 0, "Added user " . $user_record {userid};
279 my $cvs_server = shift;
280 my $repository = shift;
281 my $filename = shift;
285 #my $logfile = "/tmp/commit.log";
286 my $logfile = "/dev/null";
288 my $CVSROOT = "$cvs_server/$repository/CVSROOT";
289 my $cvsroot = ":pserver:cvsroot\@$cvs_server:/cvs/$repository";
292 or DisplayError "Unable to chdir to $CVSROOT";
294 open FILE, ">$filename"
295 or DisplayError "Unable to open file $filename";
297 foreach (@filedata) {
303 my $status = system "cvs -d $cvsroot commit -m \"$message\" $filename > $logfile 2>&1";
305 DisplayError "Unable to commit $filename (Status: $status)" if $status ne 0;
308 or DisplayError "Unable to chdir ../../..";
311 sub CVSRepositories {
312 my $cvs_server = shift;
314 my %cvs_servers = CVSServers;
316 return sort @{$cvs_servers {$cvs_server}};
322 open CVSADM_CONF, $cvsadm_conf
323 or DisplayError "Unable to open $cvsadm_conf - $!";
325 my @lines = grep {!/^#/} <CVSADM_CONF>;
328 my ($server, $repository) = split;
330 if (defined $cvs_servers {$server}) {
331 my @repositories = @{$cvs_servers {$server}};
332 push @{$cvs_servers {$server}}, $repository;
334 push @{$cvs_servers {$server}}, $repository;
342 # Checkout or update @cvs_files in $cvs_server, $repository
343 my $cvs_server = shift;
344 my $repository = shift;
347 my $CVSROOT = "$cvs_server/$repository/CVSROOT";
348 my $cvsroot = ":pserver:$cvs_server:/cvs/$repository";
350 my $logfile = "/tmp/checkout.log";
354 # Filestore for this repository does not exist. Create it and
356 $status = system "mkdir -p $CVSROOT";
358 DisplayError "Unable to create directory $CVSROOT (Status: $status)" if $status ne 0;
360 chdir "$cvs_server/$repository"
361 or DisplayError "Unable to chdir to $cvs_server/$repository";
363 $status = system "cvs -d $cvsroot checkout CVSROOT > $logfile 2>&1";
365 DisplayError "Unable to checkout $cvs_server/$repository/CVSROOT" if $status ne 0;
368 or DisplayError "Unable to chdir ../..";
373 chdir "$cvs_server/$repository"
374 or DisplayError "Unable to chdir to $cvs_server/$repository";
376 foreach (@cvsfiles) {
377 # There may be no readers or writers files. Attempt to check them
378 # out but allow failures to happen without increasing $status
379 if ($_ eq "readers" or $_ eq "writers") {
380 if (!-f "CVSROOT/$_") {
381 system "cvs -d $cvsroot checkout CVSROOT/$_ >> $logfile 2>&1";
383 $status += system "cvs -d $cvsroot update CVSROOT/$_ >> $logfile 2>&1";
386 $status += system "cvs -d $cvsroot checkout CVSROOT/$_ >> $logfile 2>&1";
391 or DisplayError "Unable to chdir ../..";
397 my $cvs_server = shift;
398 my $repository = shift;
401 # Do not allow the cvsadm group to be deleted
402 return 1, "You cannot delete the cvsadm group" if $group eq "cvsadm";
404 my $groups = "$cvs_server/$repository/CVSROOT/groups";
408 my @groups = Read $groups;
415 next if $group eq $line;
417 push @new_groups, "$line\n";
420 CVSCommit $cvs_server, $repository, "groups", "Removed group $group", sort (@new_groups);
424 # Remove mention of this group from passwd file
425 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
429 my @lines = Read $passwd;
436 my @fields = split /:/, $line;
438 if ($fields [5] !~ /$group/) {
439 push @new_lines, "$line\n";
445 my @old_groups = split /,/, $fields [5];
448 foreach (@old_groups) {
449 push @groups, $_ if $_ ne $group;
472 push @new_lines, $passwd_line;
475 CVSCommit $cvs_server, $repository, "passwd", "Removed references to group $group from passwd", sort (@new_lines);
479 return 0, "Deleted group $group";
483 my $cvs_server = shift;
484 my $repository = shift;
487 # Do not allow the cvsroot sysuser to be deleted
488 return 1, "You cannot delete the cvsroot system user" if $sysuser eq "cvsroot";
490 my $sysusers = "$cvs_server/$repository/CVSROOT/sysusers";
494 my @sysusers = Read $sysusers;
497 foreach (@sysusers) {
501 next if $sysuser eq $line;
503 push @new_sysusers, "$line\n";
506 CVSCommit $cvs_server, $repository, "sysusers", "Removed sysuser $sysuser", sort (@new_sysusers);
510 return 0, "Deleted system user $sysuser";
514 my $cvs_server = shift;
515 my $repository = shift;
518 # Do not allow the cvsroot user to be deleted
519 return 1, "You cannot delete the cvsroot user" if $userid eq "cvsroot";
521 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
525 my @passwd = Read $passwd;
532 my @fields = split /:/, $line;
534 next if $fields [0] eq $userid;
536 push @new_passwd, "$line\n";
539 my $euid = cookie "CVSAdmUser";
542 "Removed user $userid" :
543 "$euid removed user $userid";
545 CVSCommit $cvs_server, $repository, "passwd", "Removed user $userid", sort (@new_passwd);
549 return 0, "Deleted user $userid";
556 if (!$heading_done) {
557 # Put out a header so we can display the error message
561 "CVSAdm: Error: $errmsg",
562 "CVSAdm: Error: $errmsg",
567 print h3 ({-class => "error",
568 -align => "center"}, "ERROR: " . $errmsg);
570 if (!defined $status) {
579 print h3 ({-class => "msg",
580 -align => "center"}, $msg);
584 my $table_name = shift;
586 # General footing (copyright). Note we calculate the current year
587 # so that the copyright automatically extends itself.
588 my $year = substr ((scalar (localtime)), 20, 4);
590 print start_div {-class => "copyright"};
591 print "Copyright © ",
592 a ({-href => "http://defaria.com"},
594 " $year - All rights reserved";
597 print end_div; # This div ends "content" which was started in Heading
598 print "<script language='JavaScript1.2'>AdjustTableWidth (\"$table_name\");</script>"
599 if defined $table_name;
604 my $cvs_server = shift;
605 my $repository = shift;
607 my $groups = "$cvs_server/$repository/CVSROOT/groups";
609 my @lines = Read $groups;
621 # This subroutine puts out the header for web pages. It is called by
622 # various cgi scripts thus has a few parameters.
623 my $action = shift; # One of getcookie, setcookie, unsetcookie
624 my $userid = shift; # User id (if setting a cookie)
625 my $title = shift; # Title string
626 my $h1 = shift; # H1 header
627 my $h2 = shift; # H2 header (optional)
628 my $table_name = shift; # Name of table in page, if any
633 # Incorporate CVSAdmUtils.js
634 push @java_scripts, [
635 {-language => "JavaScript1.2",
636 -src => "CVSAdmUtils.js"}];
638 # Since Heading is called from various scripts we sometimes need to
639 # set a cookie, other times delete a cookie but most times return the
641 if ($action eq "getcookie") {
642 # Get userid from cookie
643 $userid = cookie ("CVSAdmUser");
644 } elsif ($action eq "setcookie") {
646 -name => "CVSAdmUser",
651 } elsif ($action eq "unsetcookie") {
653 -name => "CVSAdmUser",
661 header (-title => "$title",
664 if (defined $table_name) {
666 start_html (-title => "$title",
667 -author => "ADeFaria\@lnxw.com",
668 -style => {-src => "CVSAdmStyle.css"},
669 -onResize => "AdjustTableWidth (\"$table_name\");",
671 Link ({-rel => "icon",
672 -href => "http://wwww.lynuxworks.com/favicon.ico"})
674 -script => @java_scripts);
677 start_html (-title => "$title",
678 -author => "ADeFaria\@lnxw.com",
679 -style => {-src => "CVSAdmStyle.css"},
681 Link ({-rel => "icon",
682 -href => "http://wwww.lynuxworks.com/favicon.ico"})
684 -script => @java_scripts);
687 print start_div {class => "heading"};
688 # if (defined $userid and $userid ne "") {
689 # $h1 .= " (userid: $userid)";
691 # $h1 .= " (userid: undefined)";
694 print h2 {-align => "center",
698 # if ($action eq "setcookie") {
699 # $h2 .= " - Set CVSAdmUser to $userid";
700 # } elsif ($action eq "unsetcookie") {
701 # $h2 .= " - Unset CVSAdmUser";
703 # $h2 .= " - Action = $action";
706 if (defined $h2 && $h2 ne "") {
707 print h3 {-align => "center",
714 print start_div {-class => "content"};
720 ### CVS Read/Write access ##############################################
721 # CVS decides read/write access based on the presence of the user name
722 # in the files readers and writers in the repository. Additionally
723 # either or both of these files may be missing.
725 # The CVS Manual says:
726 # If `readers' exists, and this user is listed in it, then she
727 # gets read-only access. Or if `writers' exists, and this user
728 # is NOT listed in it, then she also gets read-only access (this
729 # is true even if `readers' exists but she is not listed
730 # there). Otherwise, she gets full read-write access.
732 # Of course there is a conflict if the user is listed in both
733 # files. This is resolved in the more conservative way, it being
734 # better to protect the repository too much than too little:
735 # such a user gets read-only access.
737 # Based on that the following describe the access granted to a user.
739 # case readers writers read access write access
740 # ---- ----------- ----------- ----------- ------------
741 # 1 No File No File No No
742 # 2 No File Not Present Yes No
743 # 3 No File Present Yes Yes
744 # 4 Not Present No File No No
745 # 5 Not Present Not Present Yes No
746 # 6 Not Present Present Yes Yes
747 # 7 Present No File Yes No
748 # 8 Present Not Present Yes No
749 # 9 Present Present Yes No
751 # Case 1: A strict intepretation of the CVS manual might lead you to
752 # believe that since readers does not exist and writers does not exist
753 # then it would fall into the "Otherwise" statement at the end of the
754 # first paragraph. However an argument can be made that the user is
755 # also not listed in the writers file because the writers file is not
756 # present. But I believe that no access should be granted.
758 # Case 2: Readers does not exist and the user is not listed in writers
759 # so read only access.
761 # Case 3: Readers does not exist but the user is listed in writers. So
762 # the user has write access. Does this imply read access? Does
763 # write-only access exist?
765 # Case 4: User is not listed in the readers file and there is no writers
766 # file. This case is not covered by the CVS manual. My assumption is
767 # therefore no access. Again a strict interpretation might argue the
768 # "Otherwise" clause but I think not.
770 # Case 5: User is not listed in the readers file nor in the writers file
771 # therefore read only access.
773 # Case 6: User is not listed in the readers file but is listed in the
774 # writers file. User gets read/write access.
776 # Case 7: User is listed in the readers file but there is no writers
777 # file. Read only access.
779 # Case 8: User is listed in the readers file but not present in writers
780 # file. Read only access.
782 # Case 9: User is listed in the readers file and the writers file. This
783 # is the conflict. Resolve the conflict by only providing read access.
784 ### CVS Read/Write access ##############################################
789 return 0 if !-f $file;
791 my @lines = Read $file;
796 return 2 if $line eq $userid;
803 my $cvs_server = shift;
804 my $repository = shift;
807 return 0 if !defined $userid;
808 return 1 if $userid eq "cvsroot";
810 return UserInGroup ($cvs_server, $repository, $userid, "cvsadm");
814 my $cvs_server = shift;
815 my $repository = shift;
818 my $reader_status = InFile $userid, "$cvs_server/$repository/CVSROOT/readers";
819 my $writer_status = InFile $userid, "$cvs_server/$repository/CVSROOT/writers";
821 if ($reader_status eq 0) {
823 if ($writer_status eq 0) {
825 return 0; # Read access denied
826 } elsif ($writer_status eq 1) {
827 # Userid is not present in writers file
828 return 1; # Read access granted
830 # Userid is present in writers file (implied read access)
831 return 1; # Read access granted
833 } elsif ($reader_status eq 1) {
834 # Userid is not in readers file
835 if ($writer_status eq 0) {
837 return 0; # Read access denied
838 } elsif ($writer_status eq 1) {
839 # Userid is not present in writers file
840 return 1; # Read access granted
842 # Userid is present in writers file (implied read access)
843 return 1; # Read access granted
846 # Userid is present in readers file
847 if ($writer_status eq 0) {
848 return 1; # Read access granted
849 } elsif ($writer_status eq 1) {
850 return 1; # Read access granted
852 return 1; # Read access granted
858 my $cvs_server = shift;
859 my $repository = shift;
862 my $reader_status = InFile $userid, "$cvs_server/$repository/CVSROOT/readers";
863 my $writer_status = InFile $userid, "$cvs_server/$repository/CVSROOT/writers";
865 if ($reader_status eq 0) {
867 if ($writer_status eq 0) {
869 return 0; # Write access denied
870 } elsif ($writer_status eq 1) {
871 # Userid is not present in writers file
872 return 0; # Write access denied
874 # Userid is present in writers file
875 return 1; # Write access granted
877 } elsif ($reader_status eq 1) {
878 # Userid is not in readers file
879 if ($writer_status eq 0) {
881 return 0; # Write access denied
882 } elsif ($writer_status eq 1) {
883 # Userid is not present in writers file
884 return 0; # Write access denied
886 # Userid is present in writers file
887 return 1; # Write access granted
890 # Userid is present in readers file
891 if ($writer_status eq 0) {
892 return 0; # Write access denied
893 } elsif ($writer_status eq 1) {
894 return 0; # Write access denied
896 return 0; # Write access denied
904 flock $file, LOCK_EX;
908 my $cvs_server = shift;
909 my $repository = shift;
910 my $username = shift;
911 my $password = shift;
913 my %passwd = OpenPasswd $cvs_server, $repository;
915 if (!defined $passwd {$username}) {
919 my %fields = %{$passwd {$username}};
921 my $salt = substr $fields {password}, 0, 2;
923 $password = crypt $password, $salt;
925 if ($fields {password} eq $password) {
933 my $cvs_server = shift;
934 my $repository = shift;
936 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
939 # Passwd file is missing. Let's try a CVSUpdate...
940 my $status = CVSUpdate $cvs_server, $repository;
943 DisplayError "Unable to update CVSROOT! (Status: $status)";
948 my @passwd = Read $passwd;
954 my @fields = split /:/, $line;
957 $fields {password} = $fields [1];
958 $fields {system_user} = $fields [2];
959 $fields {fullname} = $fields [3];
960 $fields {email} = $fields [4];
962 # Handle groups (comma separated)
963 my @groups = split /,/, $fields [5];
964 $fields {groups} = \@groups;
965 $passwd {$fields [0]} = \%fields;
972 my $cvs_server = shift;
973 my $repository = shift;
976 DisplayError "Userid not defined" if !defined $userid;
978 my %passwd = OpenPasswd $cvs_server, $repository;
980 if (!defined $userid or !defined $passwd {$userid}) {
983 return %{$passwd {$userid}};
988 my $cvs_server = shift;
989 my $repository = shift;
992 my %passwd = OpenPasswd $cvs_server, $repository;
994 my %fields = %{$passwd {$userid}};
996 return $fields {system_user}
1000 my $cvs_server = shift;
1001 my $repository = shift;
1003 my $sysusers = "$cvs_server/$repository/CVSROOT/sysusers";
1005 my @lines = Read $sysusers;
1019 flock $file, LOCK_UN;
1023 my $filename = shift;
1025 open FILE, $filename
1026 or DisplayError "Unable to open file $filename - $!";
1036 my $cvs_server = shift;
1037 my $repository = shift;
1041 my $filename = $file;
1042 $file = "$cvs_server/$repository/CVSROOT/$file";
1044 return if !-f $file;
1048 my @lines = Read $file;
1055 next if $line eq $userid;
1057 push @new_lines, "$line\n";
1060 my $euid = cookie "CVSAdmUser";
1062 $euid eq "cvsroot" ?
1064 "$euid removed $userid";
1066 CVSCommit $cvs_server, $repository, $filename, $commit_msg, sort (@new_lines);
1072 my $cvs_server = shift;
1073 my $repository = shift;
1077 if ($access eq "r") {
1078 Remove $cvs_server, $repository, "writers", $userid;
1079 Add $cvs_server, $repository, "readers", $userid;
1080 } elsif ($access eq "rw") {
1081 Remove $cvs_server, $repository, "readers", $userid;
1082 Add $cvs_server, $repository, "writers", $userid;
1084 Remove $cvs_server, $repository, "readers", $userid;
1085 Remove $cvs_server, $repository, "writers", $userid;
1090 my $cvs_server = shift;
1091 my $repository = shift;
1092 my $old_group = shift;
1093 my $new_group = shift;
1095 # CVS readers and writers files are a little weird. We will attempt
1096 # to simplify here. If a user has read only access to a repository
1097 # then we will explicitly list them in the readers file and make
1098 # sure they are not in the writers file. If they have write access
1099 # (thus implying read access) then we will arrange for them to be in
1100 # the writers file and absent from the readers file as CVS treats
1101 # users who are in both files as read only.
1102 my $groups = "$cvs_server/$repository/CVSROOT/groups";
1106 my @groups = Read $groups;
1113 if ($line eq $old_group) {
1114 push @new_groups, "$new_group\n";
1116 push @new_groups, "$line\n";
1120 CVSCommit $cvs_server, $repository, "groups", "Changed $old_group -> $new_group in $groups", sort (@new_groups);
1124 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
1128 my @passwd = Read $passwd;
1135 my @fields = split /:/, $line;
1136 my @groups = split /,/, $fields [5];
1140 foreach (my $group = @groups) {
1143 if ($group eq $old_group) {
1144 push @new_groups, "$new_group\n";
1146 push @new_groups, "$group\n";
1153 foreach (@new_groups) {
1161 $group_str .= ",$line";
1173 push @new_passwd, $passwd_line;
1176 CVSCommit $cvs_server, $repository, "passwd", "Updated $passwd changing any $old_group -> $new_group", sort (@new_passwd);
1184 my $cvs_server = shift;
1185 my $repository = shift;
1186 my $old_sysuser = shift;
1187 my $new_sysuser = shift;
1189 my $sysusers = "$cvs_server/$repository/CVSROOT/sysusers";
1193 my @sysusers = Read $sysusers;
1196 foreach (@sysusers) {
1200 if ($old_sysuser eq $line) {
1201 push @new_sysusers, "$new_sysuser\n";
1203 push @new_sysusers, "$line\n";
1207 CVSCommit $cvs_server, $repository, "sysusers", "Changed $old_sysuser -> $new_sysuser in $sysusers", sort (@new_sysusers);
1211 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
1215 my @passwd = Read $passwd;
1222 my @fields = split /:/, $line;
1224 if ($fields [2] eq $old_sysuser) {
1225 $fields [2] = $new_sysuser;
1236 push @new_passwd, $passwd_line;
1239 CVSCommit $cvs_server, $repository, "passwd", "Updated $passwd changing any $old_sysuser -> $new_sysuser", sort (@new_passwd);
1247 my $cvs_server = shift;
1248 my $repository = shift;
1249 my %user_record = @_;
1251 my $euid = cookie "CVSAdmUser";
1253 if (defined $user_record {new_password} and $user_record {new_password} ne "") {
1254 if (!IsAdmin $cvs_server, $repository, $euid) {
1255 my $status = CVSAdm::Login $cvs_server, $repository,
1256 $user_record {userid}, $user_record {old_password};
1258 DisplayError "The old password you supplied is invalid - Go back and try again";
1264 UpdateAccess $cvs_server, $repository, $user_record {userid}, $user_record {$repository};
1266 my $passwd = "$cvs_server/$repository/CVSROOT/passwd";
1270 my @passwd = Read $passwd;
1277 my @fields = split /:/, $line;
1279 if ($fields [0] eq $user_record {userid}) {
1280 if (defined $user_record {new_password} and $user_record {new_password} ne "") {
1281 my $salt = substr $fields [1], 0, 2;
1282 $user_record {password} = crypt $user_record {new_password}, $salt;
1284 $user_record {password} = $fields [1];
1287 $user_record {system_user} = $fields [2] if !defined $user_record {system_user};
1289 $line = $user_record {userid} . ":" .
1290 $user_record {password} . ":" .
1291 $user_record {system_user} . ":" .
1292 $user_record {fullname} . ":" .
1293 $user_record {email} . ":" .
1294 $user_record {groups};
1297 push @new_passwd, "$line\n";
1300 my $euid = cookie "CVSAdmUser";
1302 $euid eq "cvsroot" ?
1303 "Changed " . $user_record {userid} . " entry" :
1304 "$euid changed " . $user_record {userid} . " entry";
1306 CVSCommit $cvs_server, $repository, "passwd", $commit_msg, sort (@new_passwd);
1314 my $cvs_server = shift;
1315 my $repository = shift;
1319 my %user_fields = PasswdEntry $cvs_server, $repository, $userid;
1321 return 0 if !defined $user_fields {groups};
1323 my @user_groups = @{$user_fields {groups}};
1325 foreach (@user_groups) {
1329 return 1 if $group eq $line;
1336 my $cvs_server = shift;
1337 my $repository = shift;
1341 my %passwd = OpenPasswd $cvs_server, $repository;
1343 foreach (keys %passwd) {