Merge branch 'ph/stash-rerere' into maint-1.7.11
[git] / perl / Git / SVN / Prompt.pm
1 package Git::SVN::Prompt;
2 use strict;
3 use warnings;
4 require SVN::Core;
5 use vars qw/$_no_auth_cache $_username/;
6
7 sub simple {
8         my ($cred, $realm, $default_username, $may_save, $pool) = @_;
9         $may_save = undef if $_no_auth_cache;
10         $default_username = $_username if defined $_username;
11         if (defined $default_username && length $default_username) {
12                 if (defined $realm && length $realm) {
13                         print STDERR "Authentication realm: $realm\n";
14                         STDERR->flush;
15                 }
16                 $cred->username($default_username);
17         } else {
18                 username($cred, $realm, $may_save, $pool);
19         }
20         $cred->password(_read_password("Password for '" .
21                                        $cred->username . "': ", $realm));
22         $cred->may_save($may_save);
23         $SVN::_Core::SVN_NO_ERROR;
24 }
25
26 sub ssl_server_trust {
27         my ($cred, $realm, $failures, $cert_info, $may_save, $pool) = @_;
28         $may_save = undef if $_no_auth_cache;
29         print STDERR "Error validating server certificate for '$realm':\n";
30         {
31                 no warnings 'once';
32                 # All variables SVN::Auth::SSL::* are used only once,
33                 # so we're shutting up Perl warnings about this.
34                 if ($failures & $SVN::Auth::SSL::UNKNOWNCA) {
35                         print STDERR " - The certificate is not issued ",
36                             "by a trusted authority. Use the\n",
37                             "   fingerprint to validate ",
38                             "the certificate manually!\n";
39                 }
40                 if ($failures & $SVN::Auth::SSL::CNMISMATCH) {
41                         print STDERR " - The certificate hostname ",
42                             "does not match.\n";
43                 }
44                 if ($failures & $SVN::Auth::SSL::NOTYETVALID) {
45                         print STDERR " - The certificate is not yet valid.\n";
46                 }
47                 if ($failures & $SVN::Auth::SSL::EXPIRED) {
48                         print STDERR " - The certificate has expired.\n";
49                 }
50                 if ($failures & $SVN::Auth::SSL::OTHER) {
51                         print STDERR " - The certificate has ",
52                             "an unknown error.\n";
53                 }
54         } # no warnings 'once'
55         printf STDERR
56                 "Certificate information:\n".
57                 " - Hostname: %s\n".
58                 " - Valid: from %s until %s\n".
59                 " - Issuer: %s\n".
60                 " - Fingerprint: %s\n",
61                 map $cert_info->$_, qw(hostname valid_from valid_until
62                                        issuer_dname fingerprint);
63         my $choice;
64 prompt:
65         print STDERR $may_save ?
66               "(R)eject, accept (t)emporarily or accept (p)ermanently? " :
67               "(R)eject or accept (t)emporarily? ";
68         STDERR->flush;
69         $choice = lc(substr(<STDIN> || 'R', 0, 1));
70         if ($choice =~ /^t$/i) {
71                 $cred->may_save(undef);
72         } elsif ($choice =~ /^r$/i) {
73                 return -1;
74         } elsif ($may_save && $choice =~ /^p$/i) {
75                 $cred->may_save($may_save);
76         } else {
77                 goto prompt;
78         }
79         $cred->accepted_failures($failures);
80         $SVN::_Core::SVN_NO_ERROR;
81 }
82
83 sub ssl_client_cert {
84         my ($cred, $realm, $may_save, $pool) = @_;
85         $may_save = undef if $_no_auth_cache;
86         print STDERR "Client certificate filename: ";
87         STDERR->flush;
88         chomp(my $filename = <STDIN>);
89         $cred->cert_file($filename);
90         $cred->may_save($may_save);
91         $SVN::_Core::SVN_NO_ERROR;
92 }
93
94 sub ssl_client_cert_pw {
95         my ($cred, $realm, $may_save, $pool) = @_;
96         $may_save = undef if $_no_auth_cache;
97         $cred->password(_read_password("Password: ", $realm));
98         $cred->may_save($may_save);
99         $SVN::_Core::SVN_NO_ERROR;
100 }
101
102 sub username {
103         my ($cred, $realm, $may_save, $pool) = @_;
104         $may_save = undef if $_no_auth_cache;
105         if (defined $realm && length $realm) {
106                 print STDERR "Authentication realm: $realm\n";
107         }
108         my $username;
109         if (defined $_username) {
110                 $username = $_username;
111         } else {
112                 print STDERR "Username: ";
113                 STDERR->flush;
114                 chomp($username = <STDIN>);
115         }
116         $cred->username($username);
117         $cred->may_save($may_save);
118         $SVN::_Core::SVN_NO_ERROR;
119 }
120
121 sub _read_password {
122         my ($prompt, $realm) = @_;
123         my $password = '';
124         if (exists $ENV{GIT_ASKPASS}) {
125                 open(PH, "-|", $ENV{GIT_ASKPASS}, $prompt);
126                 $password = <PH>;
127                 $password =~ s/[\012\015]//; # \n\r
128                 close(PH);
129         } else {
130                 print STDERR $prompt;
131                 STDERR->flush;
132                 require Term::ReadKey;
133                 Term::ReadKey::ReadMode('noecho');
134                 while (defined(my $key = Term::ReadKey::ReadKey(0))) {
135                         last if $key =~ /[\012\015]/; # \n\r
136                         $password .= $key;
137                 }
138                 Term::ReadKey::ReadMode('restore');
139                 print STDERR "\n";
140                 STDERR->flush;
141         }
142         $password;
143 }
144
145 1;
146 __END__
147
148 Git::SVN::Prompt - authentication callbacks for git-svn
149
150 =head1 SYNOPSIS
151
152     use Git::SVN::Prompt qw(simple ssl_client_cert ssl_client_cert_pw
153                             ssl_server_trust username);
154     use SVN::Client ();
155
156     my $cached_simple = SVN::Client::get_simple_provider();
157     my $git_simple = SVN::Client::get_simple_prompt_provider(\&simple, 2);
158     my $cached_ssl = SVN::Client::get_ssl_server_trust_file_provider();
159     my $git_ssl = SVN::Client::get_ssl_server_trust_prompt_provider(
160         \&ssl_server_trust);
161     my $cached_cert = SVN::Client::get_ssl_client_cert_file_provider();
162     my $git_cert = SVN::Client::get_ssl_client_cert_prompt_provider(
163         \&ssl_client_cert, 2);
164     my $cached_cert_pw = SVN::Client::get_ssl_client_cert_pw_file_provider();
165     my $git_cert_pw = SVN::Client::get_ssl_client_cert_pw_prompt_provider(
166         \&ssl_client_cert_pw, 2);
167     my $cached_username = SVN::Client::get_username_provider();
168     my $git_username = SVN::Client::get_username_prompt_provider(
169         \&username, 2);
170
171     my $ctx = new SVN::Client(
172         auth => [
173             $cached_simple, $git_simple,
174             $cached_ssl, $git_ssl,
175             $cached_cert, $git_cert,
176             $cached_cert_pw, $git_cert_pw,
177             $cached_username, $git_username
178         ]);
179
180 =head1 DESCRIPTION
181
182 This module is an implementation detail of the "git svn" command.
183 It implements git-svn's authentication policy.  Do not use it unless
184 you are developing git-svn.
185
186 The interface will change as git-svn evolves.
187
188 =head1 DEPENDENCIES
189
190 L<SVN::Core>.
191
192 =head1 SEE ALSO
193
194 L<SVN::Client>.
195
196 =head1 INCOMPATIBILITIES
197
198 None reported.
199
200 =head1 BUGS
201
202 None.