File Coverage

blib/lib/Cvs/Cvsroot.pm
Criterion Covered Total %
statement 24 92 26.0
branch 6 34 17.6
condition 1 8 12.5
subroutine 5 17 29.4
pod 1 2 50.0
total 37 153 24.1


line stmt bran cond sub pod time code
1             package Cvs::Cvsroot;
2              
3 9     9   51 use strict;
  9         17  
  9         515  
4 9     9   14896 use File::Temp;
  9         186264  
  9         2315  
5 9     9   89 use base qw(Class::Accessor);
  9         20  
  9         16032  
6              
7             Cvs::Cvsroot->mk_accessors(qw(cvsroot method password fingerprint passphrase));
8              
9             sub new
10             {
11 4     4 1 56 my($proto, $cvsroot, %conf) = @_;
12              
13 4 50       105 return unless defined $cvsroot;
14 4   33     88 my $class = ref $proto || $proto;
15 4         27 my $self = {};
16 4         33 bless($self, $class);
17              
18 4         178 $self->{cvsroot} = $cvsroot;
19              
20 4 50       155 if($cvsroot =~ /^\//)
    0          
    0          
21             {
22 4         47 $self->{method} = 'filesystem';
23             }
24             elsif($cvsroot =~ /^\:pserver\:/)
25             {
26 0         0 $self->{method} = 'pserver';
27 0         0 $self->{password} = $conf{password};
28             }
29             elsif($cvsroot =~ /^(?:.*\@)?.+:.*$/)
30             {
31 0   0     0 $conf{'remote-shell'} ||= 'ssh';
32 0 0       0 if($conf{'remote-shell'} =~ /(ssh|rsh)$/)
33             {
34 0         0 $self->{'remote-shell'} = $conf{'remote-shell'};
35 0         0 $self->{method} = $1;
36 0 0       0 if($self->{method} eq 'ssh')
37             {
38 0         0 $self->{password} = $conf{password};
39 0         0 $self->{fingerprint} = $conf{fingerprint};
40 0         0 $self->{passphrase} = $conf{passphrase};
41             }
42             }
43             else
44             {
45 0         0 warn 'unknown remote-shell: ' . $conf{'remote-shell'};
46 0         0 return;
47             }
48             }
49             else
50             {
51 0         0 warn "not implemented cvsroot method: $cvsroot";
52 0         0 return;
53             }
54              
55 4         48 return $self;
56             }
57              
58             sub bind
59             {
60 4     4 0 18 my($self, $cmd) = @_;
61              
62 4         21 my $debug = $cmd->cvs->debug();
63 4 50       103 if($debug)
64             {
65 0         0 print STDERR "Binding CVSROOT handlers\n";
66 0         0 print STDERR "CVSROOT access method is: $self->{method}\n";
67             }
68 4         27 my $init_context = $cmd->initial_context();
69              
70 4 50       97 if(defined $self->{'remote-shell'})
71             {
72 0         0 $ENV{CVS_RSH} = $self->{'remote-shell'};
73             }
74              
75 4 50       77 if($self->{method} eq 'pserver')
    50          
76             {
77             $init_context->push_handler
78             (
79             qr/^cvs .*: used empty password; /, sub
80             {
81 0 0   0     if(defined $self->{password})
82             {
83 0 0         if($cmd->cvs->login->success())
84             {
85             # The former command failed because it wasn't
86             # logged. So we need to relaunch it internally
87 0           $cmd->restart();
88             }
89             else
90             {
91 0           $cmd->err_result('pserver login failure');
92 0           return $init_context->finish();
93             }
94             }
95             else
96             {
97 0           $cmd->err_result('you have to login.');
98 0           return $init_context->finish();
99             }
100             }
101 0           );
102             }
103             elsif($self->{method} eq 'ssh')
104             {
105             # without pty, ssh call the ssh-askpass program to grab needed
106             # informations from user. In batch mode it's not possible, so
107             # we rewrite an ssh-askpass in a shell script stored in a
108             # temporary file and we tell ssh to call it.
109 0 0         my($fh, $file) = File::Temp::tmpnam()
110             or die "can't create a temporary file";
111 0 0         print STDERR "Creating askpass script `$file'\n"
112             if $debug;
113 0           chmod(0700, $file);
114 0           $fh->print("#!/bin/sh\n");
115 0           $fh->print("echo \$1|grep -iq password&&echo $self->{password}&&exit\n");
116 0           $fh->print("echo \$1|grep -iq passphrase&&echo $self->{passphrase}&&exit\n");
117 0           $fh->print("echo yes\n");
118 0           $fh->close();
119             $cmd->push_cleanup(sub
120             {
121 0 0   0     print STDERR "Deleting askpass script `$file'\n"
122             if $debug;
123 0           unlink $file
124 0           });
125 0           $ENV{SSH_ASKPASS} = $file;
126             # ssh doesn't tell ssh-askpass until the DISPLAY environment
127             # isn't set, so we have to set it to something (see ssh's
128             # manual for more details).
129 0           $ENV{DISPLAY} = '';
130              
131 0           my $ssh_context = $cmd->new_context();
132 0           my $fingerprint;
133              
134             # building a combo pattern for all ssh error starting with the
135             # string "ssh: "
136 0           my $error_patterns = join
137             ('|',
138             '.*: Name or service not known',
139             'connect to address [\d.]+ port \d+: Connection refused',
140             );
141             $init_context->push_handler
142             (
143             qr/^ssh: (?:$error_patterns)/, sub
144             {
145 0     0     $cmd->err_result(shift->[0]);
146 0           return $init_context->finish();
147             }
148 0           );
149              
150             $init_context->push_handler
151             (
152             qr/Could not create directory/, sub
153 0     0     {
154             # Hint: this can happened where the home directory isn't writable
155             }
156 0           );
157             $init_context->push_handler
158             (
159             qr/^Enter passphrase for key/, sub
160             {
161 0     0     $cmd->send($self->{passphrase});
162             }
163 0           );
164              
165             $init_context->push_handler
166             (
167             # maybe ssh version defendant...
168             qr/'s password:/, sub
169             {
170 0     0     $cmd->send("$self->{password}\n");
171             }
172 0           );
173             $init_context->push_handler
174             (
175             qr/Permission denied/, sub
176             {
177 0     0     $cmd->err_result('ssh: authentication failure');
178 0           return $init_context->finish();
179             }
180 0           );
181             $init_context->push_handler
182             (
183             qr/^The authenticity of host .* can't be established\./, sub
184             {
185 0     0     return $ssh_context;
186             }
187 0           );
188             $ssh_context->push_handler
189             (
190             qr/key fingerprint is ([a-f\d:]+)\./, sub
191             {
192 0     0     $fingerprint = shift->[1];
193             }
194 0           );
195             $ssh_context->push_handler
196             (
197             qr/^Are you sure you want to continue connecting/, sub
198             {
199 0 0 0 0     if(defined $fingerprint && defined $self->{fingerprint})
200             {
201 0 0         if($fingerprint eq $self->{fingerprint})
202             {
203 0           $cmd->send("yes\n");
204             }
205             else
206             {
207 0           $cmd->send("no\n");
208             }
209             }
210             else
211             {
212 0           $cmd->send("yes\n");
213             }
214             }
215 0           );
216             $ssh_context->push_handler
217             (
218             qr/Host key verification failed\./, sub
219             {
220 0     0     $cmd->err_result('ssh: '.shift->[0]);
221 0           return $ssh_context->finish();
222             }
223 0           );
224             $ssh_context->push_handler
225             (
226             qr/Warning: Permanently added .* to the list of known hosts\./, sub
227             {
228             # fallback to initial context
229 0     0     return $init_context;
230             }
231 0           );
232             }
233             }
234              
235             1;
236             =pod
237              
238             =head1 LICENCE
239              
240             This library is free software; you can redistribute it and/or modify
241             it under the terms of the GNU Lesser General Public License as
242             published by the Free Software Foundation; either version 2.1 of the
243             License, or (at your option) any later version.
244              
245             This library is distributed in the hope that it will be useful, but
246             WITHOUT ANY WARRANTY; without even the implied warranty of
247             MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
248             Lesser General Public License for more details.
249              
250             You should have received a copy of the GNU Lesser General Public
251             License along with this library; if not, write to the Free Software
252             Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
253             USA
254              
255             =head1 COPYRIGHT
256              
257             Copyright (C) 2003 - Olivier Poitrey
258