#!/usr/bin/perl use strict; use warnings; use Getopt::Long; use File::Basename; my $VERSION = "0.1"; my %options = ( help => 0, debug => 0, verbose => 0, insecure => 0, file => [], # identical token maps, e.g. host -> host, will be inserted later tmap => { port => 'protocol', machine => 'host', path => 'path', login => 'username', user => 'username', password => 'password', } ); # Map each credential protocol token to itself on the netrc side. foreach (values %{$options{tmap}}) { $options{tmap}->{$_} = $_; } # Now, $options{tmap} has a mapping from the netrc format to the Git credential # helper protocol. # Next, we build the reverse token map. # When $rmap{foo} contains 'bar', that means that what the Git credential helper # protocol calls 'bar' is found as 'foo' in the netrc/authinfo file. Keys in # %rmap are what we expect to read from the netrc/authinfo file. my %rmap; foreach my $k (keys %{$options{tmap}}) { push @{$rmap{$options{tmap}->{$k}}}, $k; } Getopt::Long::Configure("bundling"); # TODO: maybe allow the token map $options{tmap} to be configurable. GetOptions(\%options, "help|h", "debug|d", "insecure|k", "verbose|v", "file|f=s@", ); if ($options{help}) { my $shortname = basename($0); $shortname =~ s/git-credential-//; print <{machine}) { next; } if (defined $nentry->{port} && $nentry->{port} =~ m/^\d+$/) { $num_port = $nentry->{port}; delete $nentry->{port}; } # create the new entry for the credential helper protocol $entry{$options{tmap}->{$_}} = $nentry->{$_} foreach keys %$nentry; # for "host X port Y" where Y is an integer (captured by # $num_port above), set the host to "X:Y" if (defined $entry{host} && defined $num_port) { $entry{host} = join(':', $entry{host}, $num_port); } push @entries, \%entry; } return @entries; } sub net_netrc_loader { my $fh = shift @_; my @entries; my ($mach, $macdef, $tok, @tok); LINE: while (<$fh>) { undef $macdef if /\A\n\Z/; if ($macdef) { next LINE; } s/^\s*//; chomp; while (length && s/^("((?:[^"]+|\\.)*)"|((?:[^\\\s]+|\\.)*))\s*//) { (my $tok = $+) =~ s/\\(.)/$1/g; push(@tok, $tok); } TOKEN: while (@tok) { if ($tok[0] eq "default") { shift(@tok); $mach = { machine => undef }; next TOKEN; } $tok = shift(@tok); if ($tok eq "machine") { my $host = shift @tok; $mach = { machine => $host }; push @entries, $mach; } elsif (exists $options{tmap}->{$tok}) { unless ($mach) { log_debug("Skipping token $tok because no machine was given"); next TOKEN; } my $value = shift @tok; unless (defined $value) { log_debug("Token $tok had no value, skipping it."); next TOKEN; } # Following line added by rmerrell to remove '/' escape char in .netrc $value =~ s/\/\\/\\/g; $mach->{$tok} = $value; } elsif ($tok eq "macdef") { # we ignore macros next TOKEN unless $mach; my $value = shift @tok; $macdef = 1; } } } return @entries; } sub read_credential_data_from_stdin { # the query: start with every token with no value my %q = map { $_ => undef } values(%{$options{tmap}}); while () { next unless m/^([^=]+)=(.+)/; my ($token, $value) = ($1, $2); die "Unknown search token $token" unless exists $q{$token}; $q{$token} = $value; log_debug("We were given search token $token and value $value"); } foreach (sort keys %q) { log_debug("Searching for %s = %s", $_, $q{$_} || '(any value)'); } return \%q; } # takes the search tokens and then a list of entries # each entry is a hash reference sub find_netrc_entry { my $query = shift @_; ENTRY: foreach my $entry (@_) { my $entry_text = join ', ', map { "$_=$entry->{$_}" } keys %$entry; foreach my $check (sort keys %$query) { if (!defined $entry->{$check}) { log_debug("OK: entry has no $check token, so any value satisfies check $check"); } elsif (defined $query->{$check}) { log_debug("compare %s [%s] to [%s] (entry: %s)", $check, $entry->{$check}, $query->{$check}, $entry_text); unless ($query->{$check} eq $entry->{$check}) { next ENTRY; } } else { log_debug("OK: any value satisfies check $check"); } } return $entry; } # nothing was found return; } sub print_credential_data { my $entry = shift @_; my $query = shift @_; log_debug("entry has passed all the search checks"); TOKEN: foreach my $git_token (sort keys %$entry) { log_debug("looking for useful token $git_token"); # don't print unknown (to the credential helper protocol) tokens next TOKEN unless exists $query->{$git_token}; # don't print things asked in the query (the entry matches them) next TOKEN if defined $query->{$git_token}; log_debug("FOUND: $git_token=$entry->{$git_token}"); printf "%s=%s\n", $git_token, $entry->{$git_token}; } } sub log_verbose { return unless $options{verbose}; printf STDERR @_; printf STDERR "\n"; } sub log_debug { return unless $options{debug}; printf STDERR @_; printf STDERR "\n"; }