11 use Authen::Simple::LDAP;
17 our $VERSION = '$Revision: 2.23 $';
18 ($VERSION) = ($VERSION =~ /\$Revision: (.*) /);
20 my $p4config = $ENV{P4_CONF} || dirname (__FILE__) . '/../etc/p4.conf';
21 my $ldapconfig = $ENV{LDAP_CONF} || dirname (__FILE__) . '/../etc/LDAP.conf';
23 my %P4OPTS = GetConfig $p4config if -r $p4config;
24 my %LDAPOPTS = GetConfig $ldapconfig if -r $ldapconfig;
26 my $serviceUser = 'shared';
27 my ($domain, $password);
28 my $defaultPort = 'perforce:1666';
29 my $p4tickets = $^O =~ /win/i ? 'C:/Program Files/Devops/Perforce/p4tickets'
30 : '/opt/audience/perforce/p4tickets';
34 # If USERDOMAIN is set and equal to audience then set $domain to ''. This will
35 # use the Audience domain settings in LDAP.conf.
36 if ($ENV{USERDOMAIN}) {
37 if (lc $ENV{USERDOMAIN} eq 'audience') {
40 $domain = $ENV{USERDOMAIN}
45 my ($class, %parms) = @_;
47 my $self = bless {}, $class;
49 $self->{P4USER} = $parms{username} || $P4OPTS{P4USER} || $ENV{P4USER} || $serviceUser;
50 $self->{P4PASSWD} = $parms{password} || $P4OPTS{P4PASSWD} || $ENV{P4PASSWD} || undef;
51 $self->{P4CLIENT} = $parms{p4client} || $P4OPTS{P4CLIENT} || $ENV{P4CLIENT} || undef;
52 $self->{P4PORT} = $parms{p4port} || $ENV{P4PORT} || $defaultPort;
54 $self->{P4} = $self->connect (%parms);
60 my ($self, $cmd, $exit) = @_;
62 my $msg = "Unable to run \"p4 $cmd\"";
63 my $errors = $self->{P4}->ErrorCount;
65 error "$msg\n" . $self->{P4}->Errors, $exit if $errors;
73 $self->{P4} = P4->new;
75 $self->{P4}->SetUser ($self->{P4USER});
76 $self->{P4}->SetClient ($self->{P4CLIENT}) if $self->{P4CLIENT};
77 $self->{P4}->SetPort ($self->{P4PORT});
78 $self->{P4}->SetPassword ($self->{P4PASSWD}) unless $self->{P4USER} eq $serviceUser;
80 verbose_nolf "Connecting to Perforce server $self->{P4PORT}...";
81 $self->{P4}->Connect or croak "Unable to connect to Perforce Server\n";
84 verbose_nolf "Logging in as $self->{P4USER}\@$self->{P4PORT}...";
86 unless ($self->{P4USER} eq $serviceUser) {
87 $self->{P4}->RunLogin;
89 $self->errors ('login', $self->{P4}->ErrorCount);
91 $ENV{P4TICKETS} = $p4tickets if $self->{P4USER} eq $serviceUser;
99 sub authenticateUser ($;$) {
100 my ($self, $username, $p4client) = @_;
103 my $ad = Authen::Simple::LDAP->new (
104 host => $LDAPOPTS{AD_HOST},
105 basedn => $LDAPOPTS{AD_BASEDN},
106 port => $LDAPOPTS{AD_PORT},
107 filter => $LDAPOPTS{AD_FILTER},
110 # Read the password from <stdin> and truncate the newline - unless we already
111 # read in the password
114 $password = GetPassword;
122 # Special handling of "shared" user
123 if ($username eq 'shared') {
124 my $sharedAcl = "$FindBin::Bin/sharedAcl.txt";
126 croak "Unable to find file $sharedAcl" unless -f $sharedAcl;
128 open my $sharedAcls, '<', $sharedAcl
129 or croak "Unable to open $sharedAcl - $!";
131 chomp (my @acls = <$sharedAcls>);
139 exit if $p4client =~ /$_/;
141 exit if $_ eq $p4client;
146 # Connect to Perforce
147 $self->connect unless $self->{P4};
149 # Must be a valid Perforce user
150 return unless $self->getUser ($username);
152 # And supply a valid username/password
153 return $ad->authenticate ($username, $password);
157 my ($self, $changelist) = @_;
159 my $change = $self->{P4}->Run ('change', '-o', $changelist);
165 my ($self, $changelist) = @_;
171 my ($self, $args, %opts) = @_;
176 if (/from/i and $opts{to}) {
177 $args .= " $opts{$_},$opts{to}";
181 $args .= " $opts{$_}";
185 my $changes = $self->{P4}->Run ($cmd, $args);
187 return $self->errors ("$cmd $args") || $changes;
191 my ($self, $job) = @_;
193 my $jobs = $self->{P4}->IterateJobs ("-e $job");
195 return $self->errors ("jobs -e $job") || $job;
199 my ($self, $changelist) = @_;
201 my $change = $self->{P4}->FetchChange ($changelist);
203 return $self->errors ("change $changelist") || $change;
207 my ($self, $changelist) = @_;
209 my $files = $self->{P4}->Run ('files', "\@=$changelist");
211 return $self->errors ("files \@=$changelist") || $files;
215 my ($self, $fileSpec, %opts) = @_;
217 return $self->{P4}->RunFilelog ($fileSpec, %opts);
220 sub getRevision ($;$) {
221 my ($self, $filename, $revision) = @_;
224 if ($filename =~ /#/) {
225 ($filename, $revision) = split $filename, '#';
227 error "No revision specified in $filename";
233 my @contents = $self->{P4}->RunPrint ("$filename#$revision");
235 if ($self->{P4}->ErrorCount) {
236 $self->errors ("Print $filename#$revision");
245 my ($self, $user) = @_;
247 $user //= $ENV{P4USER} || $ENV{USER};
250 my @args = ('-o', $user);
252 my $userRecs = $self->{P4}->Run ($cmd, @args);
254 # Perforce returns an array of qualifying users. We only care about the first
255 # one. However if the username is invalid, Perforce still returns something
256 # that looks like a user. We look to see if there is a Type field here which
257 # indicates that it's a valid user
258 if ($userRecs->[0]{Type}) {
259 return %{$userRecs->[0]};
265 sub renameSwarmUser ($$) {
266 my ($self, $oldusername, $newusername) = @_;
268 # We are turning this off because Perforce support says that just modifying
269 # the keys we do not update the indexing done in the Perforce Server/Database.
270 # So instead we have a PHP script (renameUser.php) which goes through the
271 # official, but still unsupported, "Swarm Record API" to change the usernames
272 # and call the object's method "save" which should perform the necessary
273 # reindexing... Stay tuned! :-)
275 # BTW One needs to run renameUser.php by hand as we do not do that here.
278 $keys = $self->getKeys ('swarm-*') unless $keys;
283 if ($key{value} =~ /$oldusername/) {
284 $key{value} =~ s/\"$oldusername\"/\"$newusername\"/g;
285 $key{value} =~ s/\@$oldusername /\@$newusername /g;
286 $key{value} =~ s/\@$oldusername\./\@$newusername\./g;
287 $key{value} =~ s/\@$oldusername,/\@$newusername,/g;
288 $key{value} =~ s/ $oldusername / $newusername /g;
289 $key{value} =~ s/ $oldusername,/ $newusername,/g;
290 $key{value} =~ s/ $oldusername\./ $newusername\./g;
291 $key{value} =~ s/-$oldusername\"/-$newusername\"/g;
295 display "Correcting key $key{key}";
297 my @result = $self->{P4}->Run ($cmd, $key{key}, $key{value});
299 $self->errors ($cmd, $result[0]->{key} || 1);
306 sub renameUser ($$) {
307 my ($self, $old, $new) = @_;
309 my $cmd = 'renameuser';
310 my @args = ("--from=$old", "--to=$new");
312 $self->{P4}->Run ($cmd, @args);
314 my $status = $self->errors (join ' ', $cmd, @args);
316 return $status if $status;
318 # return $self->renameSwarmUser ($old, $new);
322 my ($self, %user) = @_;
324 # Trying to do this with P4Perl is difficult. First off the structure needs
325 # to be AOH and secondly you need to call SetUser to be the other user. That
326 # said you need to also specify -f to force the update (which means you must
327 # a admin (or superuser?) and I found no way to specify -f so I've reverted
328 # back to using p4 from the command line. I also don't like having to use
330 my $tmpfile = File::Temp->new;
331 my $tmpfilename = $tmpfile->filename;
333 print $tmpfile "User: $user{User}\n";
334 print $tmpfile "Email: $user{Email}\n";
335 print $tmpfile "Update: $user{Update}\n";
336 print $tmpfile "FullName: $user{FullName}\n";
340 my @lines = `p4 -p $self->{P4PORT} user -f -i < $tmpfilename`;
343 return wantarray ? @lines : join '', @lines;
347 my ($self, $filter) = @_;
357 my $keys = $self->{P4}->Run ($cmd, @args);
359 $self->errors ($cmd . join (' ', @args), 1);
365 my ($self, $name, $value) = @_;
368 my @args = ($name, $value);
370 $self->{P4}->Run ($cmd, @args);
372 return $self->errors (join ' ', $cmd, @args);