File Coverage

blib/lib/Net/Telnet/Cisco.pm
Criterion Covered Total %
statement 18 390 4.6
branch 0 262 0.0
condition 0 23 0.0
subroutine 6 36 16.6
pod 21 21 100.0
total 45 732 6.1


line stmt bran cond sub pod time code
1             package Net::Telnet::Cisco;
2              
3             #-----------------------------------------------------------------
4             # Net::Telnet::Cisco - interact with a Cisco router
5             #
6             # $Id: Cisco.pm,v 1.52 2002/06/18 17:17:03 jkeroes Exp $
7             #
8             # Todo: Add error and access logging.
9             #
10             # POD documentation at end of file.
11             #
12             #-----------------------------------------------------------------
13              
14             require 5.005;
15              
16 1     1   65286 use strict;
  1         3  
  1         30  
17 1     1   5 use warnings;
  1         2  
  1         28  
18 1     1   1027 use Net::Telnet 3.02;
  1         44559  
  1         46  
19 1     1   503 use AutoLoader;
  1         1449  
  1         6  
20 1     1   38 use Carp;
  1         2  
  1         59  
21              
22 1     1   6 use vars qw($AUTOLOAD @ISA $VERSION $DEBUG);
  1         2  
  1         5315  
23              
24             @ISA = qw(Net::Telnet);
25             $VERSION = '1.12';
26             $^W = 1;
27             $DEBUG = 0;
28             $|++;
29              
30             #------------------------------
31             # Public Methods
32             #------------------------------
33              
34             sub new {
35 0     0 1   my $class = shift;
36              
37 0           my ($self, $host, %args);
38              
39            
40 0           my %ntparams = (
41             prompt => '/(?m:^(?:[\w.\/]+\:)?[\w.-]+\s?(?:\(config[^\)]*\))?\s?[\$#>]\s?(?:\(enable\))?\s*$)/'
42             );
43 0           my %params = (
44             always_waitfor_prompt => 1,
45             autopage => 1,
46             ignore_warnings => 0,
47             last_cmd => '',
48             last_prompt => '',
49             more_prompt => '/(?m:^\s*--More--)/',
50             normalize_cmd => 1,
51             send_wakeup => 0,
52             waitfor_pause => 0.1,
53             warnings => '/(?mx:^% Unknown VPN
54             |^%IP routing table VRF.* does not exist. Create first$
55             |^%No CEF interface information
56             |^%No matching route to delete$
57             |^%Not all config may be removed and may reappear after reactivating
58             )/',
59             );
60              
61             ## Parse the args.
62 0 0         if (@_ == 1) { # one positional arg given
63 0           ($ntparams{host}) = @_;
64             } else {
65 0           %args = @_;
66              
67             ## initial set other named args.
68 0           foreach (keys %args) {
69 0 0         if (/^-?always_waitfor_prompt$/i) {
    0          
    0          
    0          
    0          
    0          
    0          
    0          
    0          
70 0           $params{always_waitfor_prompt} = $args{$_};
71             }
72             elsif (/^-?autopage$/i) {
73 0           $params{autopage} = $args{$_};
74             }
75             elsif (/^-?ignore_warnings$/i) {
76 0           $params{ignore_warnings} = $args{$_};
77             }
78             elsif (/^-?more_prompt$/i) {
79 0           $params{more_prompt} = $args{$_};
80             }
81             elsif (/^-?normalize_cmd$/i) {
82 0           $params{normalize_cmd} = $args{$_};
83             }
84             elsif (/^-?send_wakeup$/i) {
85 0           $params{send_wakeup} = $args{$_};
86             }
87             elsif (/^-?waitfor_pause$/i) {
88 0           $params{waitfor_pause} = $args{$_};
89             }
90             elsif (/^-?warnings$/i) {
91 0           $params{warnings} = $args{$_};
92             }
93             elsif (/^-?prompt$/i) {
94 0           $ntparams{prompt} = $args{$_};
95             }
96             else {
97             # pass through
98 0           $ntparams{$_} = $args{$_};
99             }
100             }
101             }
102              
103 0 0         $self = $class->SUPER::new(%ntparams) or return;
104 0           *$self->{net_telnet_cisco} = \%params;
105              
106             ## Parse all other named args.
107 0           foreach (keys %params) {
108 0 0         if (/^-?always_waitfor_prompt$/i) {
    0          
    0          
    0          
    0          
    0          
    0          
109 0           $self->always_waitfor_prompt($args{$_});
110             }
111             elsif (/^-?autopage$/i) {
112 0           $self->autopage($args{$_});
113             }
114             elsif (/^-?ignore_warnings$/i) {
115 0           $self->ignore_warnings($args{$_});
116             }
117             elsif (/^-?more_prompt$/i) {
118 0           $self->more_prompt($args{$_});
119             }
120             elsif (/^-?normalize_cmd$/i) {
121 0           $self->normalize_cmd($args{$_});
122             }
123             elsif (/^-?send_wakeup$/i) {
124 0           $self->send_wakeup($args{$_});
125             }
126             elsif (/^-?waitfor_pause$/i) {
127 0           $self->waitfor_pause($args{$_});
128             }
129             }
130              
131 0           $self;
132             } # end sub new
133              
134             # The new prompt() stores the last matched prompt for later
135             # fun 'n amusement. You can access this string via $self->last_prompt.
136             #
137             # It also parses out any router errors and stores them in the
138             # correct place, where they can be acccessed/handled by the
139             # Net::Telnet error methods.
140             #
141             # No POD docs for prompt(); these changes should be transparent to
142             # the end-user.
143             sub prompt {
144 0     0 1   my( $self, $prompt ) = @_;
145 0           my( $prev, $stream );
146              
147 0           $stream = $ {*$self}{net_telnet_cisco};
  0            
148 0           $prev = $self->SUPER::prompt;
149              
150             ## Parse args.
151 0 0         if ( @_ == 2 ) {
    0          
152 0 0         defined $prompt or $prompt = '';
153 0           $self->_match_check($prompt);
154 0           $self->SUPER::prompt($prompt);
155             } elsif (@_ > 2) {
156 0           return $self->error('usage: $obj->prompt($match_op)');
157             }
158              
159 0           return $prev;
160             } # end sub prompt
161              
162             # cmd() now parses errors and sticks 'em where they belong.
163             #
164             # This is a routerish error:
165             # routereast#show asdf
166             # ^
167             # % Invalid input detected at '^' marker.
168             #
169             # "show async" is valid, so the "d" of "asdf" raised an error.
170             #
171             # If an error message is found, the following error message
172             # is sent to Net::Telnet's error()-handler:
173             #
174             # Last command and router error:
175             #
176             #
177             sub cmd {
178 0     0 1   my $self = shift;
179 0           my $ok = 1;
180              
181 0           my $normalize = $self->normalize_cmd;
182 0           my $out = [];
183 0           my $output_ref;
184             my %ntparams;
185              
186             # Parse args
187             # pull out N::T::C specific (including -output) and pass
188             # rest to N::T in %params
189 0 0         if (@_ == 1) {
    0          
190 0           $ {*$self}{net_telnet_cisco}{last_cmd} = $_[0];
  0            
191 0           $ntparams{string} = $_[0];
192             } elsif ( @_ >= 2 ) {
193 0           my %args = @_;
194 0           for (keys(%args)) {
195 0 0         if (/^-?string$/i) {
    0          
    0          
196 0           $ {*$self}{net_telnet_cisco}{last_cmd} = $args{$_};
  0            
197 0           $ntparams{string} = $args{$_};
198             }
199             elsif (/^-?normalize_cmd$/i) {
200 0           $normalize = $args{$_};
201             }
202             elsif (/^-?output$/i) {
203 0           $output_ref = $args{$_};
204 0 0 0       if (defined($output_ref) and ref($output_ref) eq "ARRAY") {
205 0           $out = $output_ref;
206             }
207             }
208             else {
209 0           $ntparams{$_} = $args{$_};
210             }
211             }
212             }
213              
214 0           my $cmd = $ {*$self}{net_telnet_cisco}{last_cmd};
  0            
215 0           my $old_ors = $self->output_record_separator;
216 0           my $need_more = 0;
217              
218 0           while(1) {
219             # Send a space (with no newline) whenever we see a "More" prompt.
220 0 0         if ($need_more) {
221 0           $self->output_record_separator('');
222              
223             # We saw a more prompt, so put it in the command output.
224 0           my @tmp = $self->last_prompt;
225              
226             # Send the , taking care not to
227             # discard the top line.
228 0           push @tmp, $self->SUPER::cmd(String => " ", Cmd_remove_mode => 0);
229              
230 0 0         if ($normalize) {
231 0           push @$out, _normalize(@tmp);
232             } else {
233 0           push @$out, @tmp;
234             }
235             } else {
236 0           $self->output_record_separator($old_ors);
237 0           push @$out, $self->SUPER::cmd(%ntparams);
238             }
239              
240             # Look for errors in output
241 0           for ( my ($i, $lastline) = (0, '');
242             $i <= $#$out;
243             $lastline = $out->[$i++] ) {
244              
245             # This may have to be a pattern match instead.
246 0 0         if ( ( substr $out->[$i], 0, 1 ) eq '%' ) {
247 0 0         if ( $out->[$i] =~ /'\^' marker/ ) { # Typo & bad arg errors
248 0           chomp $lastline;
249 0           $self->error( join "\n",
250             "Last command and router error: ",
251             ( $self->last_prompt . $cmd ),
252             $lastline,
253             $out->[$i],
254             );
255 0           splice @$out, $i - 1, 3;
256             } else { # All other errors.
257 0           chomp $out->[$i];
258 0           $self->error( join "\n",
259             "Last command and router error: ",
260             ( $self->last_prompt . $cmd ),
261             $out->[$i],
262             );
263 0           splice @$out, $i, 2;
264             }
265 0           $ok = 0;
266 0           last;
267             }
268             }
269              
270             # Restore old settings
271 0           $self->output_record_separator($old_ors);
272              
273             # redo the while loop if we saw a More prompt.
274 0           my $more_re = $self->_re_sans_delims($self->more_prompt);
275 0 0 0       if ($self->autopage && $self->last_prompt =~ /$more_re/) {
276 0           $need_more = 1;
277             } else {
278 0           last;
279             }
280             }
281              
282 0 0         if (defined $output_ref) {
283 0 0         if (ref($output_ref) eq "SCALAR") {
    0          
284 0           $$output_ref = join "", @$out;
285             } elsif (ref($output_ref) eq "HASH") {
286 0           %$output_ref = @$out;
287             }
288             }
289              
290 0 0         return wantarray ? @$out : $ok;
291             }
292              
293              
294             # waitfor now stores prompts to $obj->last_prompt()
295             sub waitfor {
296 0     0 1   my $self = shift;
297              
298 0 0         return unless @_;
299              
300             # $all_prompts will be built into a regex that matches all currently
301             # valid prompts.
302             #
303             # -Match args will be added to this regex. The current prompt will
304             # be appended when all -Matches have been exhausted.
305 0           my $all_prompts = '';
306              
307             # Literal string matches, passed in with -String.
308 0           my @literals = ();
309              
310             # Parse the -Match => '/prompt \$' type options
311             # waitfor can accept more than one -Match argument, so we can't just
312             # hashify the args.
313 0 0         if (@_ >= 2) {
    0          
314 0           my @args = @_;
315 0           while ( my ($k, $v) = splice @args, 0, 2 ) {
316 0 0         if ($k =~ /^-?[Ss]tring$/) {
    0          
317 0           push @literals, $v;
318             } elsif ($k =~ /^-?[Mm]atch$/) {
319 0           $all_prompts = $self->_prompt_append($all_prompts, $v);
320             }
321             }
322             } elsif (@_ == 1) {
323             # A single argument is always a -match.
324 0           $all_prompts = $self->_prompt_append($all_prompts, $_[0]);
325             }
326              
327 0           my $all_re = $self->_re_sans_delims($all_prompts);
328 0           my $prompt_re = $self->_re_sans_delims($self->prompt);
329 0           my $more_re = $self->_re_sans_delims($self->more_prompt);
330              
331              
332             # Add the current prompt if it's not already there. You can turn this behavior
333             # off by setting always_waitfor_prompt to a false value.
334 0 0 0       if ($self->always_waitfor_prompt && index($all_re, $prompt_re) == -1) {
335 0 0         unshift @_, "-Match" if @_ == 1;
336 0           push @_, (-Match => $self->prompt);
337              
338 0           $all_prompts = $self->_prompt_append($all_prompts, $self->prompt);
339 0           $all_re = $self->_re_sans_delims($all_prompts);
340             }
341              
342             # Add the more prompt if it's not present. See the autopage() docs
343             # to turn this behaviour off.
344 0 0 0       if ($self->autopage && index($all_re, $more_re) == -1) {
345 0 0         unshift @_, "-Match" if @_ == 1;
346 0           push @_, (-Match => $self->more_prompt);
347              
348 0           $all_prompts = $self->_prompt_append($all_prompts, $self->more_prompt);
349 0           $all_re = $self->_re_sans_delims($all_prompts);
350             }
351              
352 0 0 0       return $self->error("Godot ain't home - waitfor() isn't waiting for anything.")
353             unless $all_prompts || @literals;
354              
355             # There's a timing issue that I can't quite figure out.
356             # Adding a small pause here seems to make it go away.
357 0           select undef, undef, undef, $self->waitfor_pause;
358              
359 0           my ($prematch, $match) = $self->SUPER::waitfor(@_);
360              
361             # If waitfor saw a prompt then store it.
362 0 0         if ($match) {
363 0           for (@literals) {
364 0 0         if (index $match, $_) {
365 0 0         return wantarray ? ($prematch, $match) : 1;
366             }
367             }
368              
369 0 0         if ($match =~ /($all_re)/m ) {
370 0           $ {*$self}{net_telnet_cisco}{last_prompt} = $1;
  0            
371 0 0         return wantarray ? ($prematch, $match) : 1;
372             }
373             }
374 0 0         return wantarray ? ( $prematch, $match ) : 1;
375             }
376              
377              
378             sub login {
379 0     0 1   my($self) = @_;
380             my(
381 0           $cmd_prompt,
382             $endtime,
383             $error,
384             $lastline,
385             $match,
386             $orig_errmode,
387             $orig_timeout,
388             $prematch,
389             $reset,
390             $timeout,
391             $usage,
392             $sent_wakeup,
393             );
394 0           my ($username, $password, $tacpass, $passcode ) = ('','','','');
395 0           my (%args, %seen);
396              
397 0           local $_;
398              
399             ## Init vars.
400 0           $timeout = $self->timeout;
401 0           $self->timed_out('');
402 0 0         return if $self->eof;
403 0           $cmd_prompt = $self->prompt;
404 0           $sent_wakeup = 0;
405              
406 0 0         print "login:\t[orig: $cmd_prompt]\n" if $DEBUG;
407              
408 0           $usage = 'usage: $obj->login([Name => $name,] [Password => $password,] '
409             . '[Passcode => $passcode,] [Prompt => $matchop,] [Timeout => $secs,])';
410              
411 0 0         if (@_ == 3) { # just username and passwd given
412 0           ($username, $password) = (@_[1,2]);
413             }
414             else { # named args given
415             ## Get the named args.
416 0           (undef, %args) = @_;
417              
418             ## Parse the named args.
419 0           foreach (keys %args) {
420 0 0         if (/^-?name$/i) {
    0          
    0          
    0          
    0          
421 0           $username = $args{$_};
422             } elsif (/^-?passw/i) {
423 0           $password = $args{$_};
424             } elsif (/^-?passcode/i) {
425 0           $passcode = $args{$_};
426             } elsif (/^-?prompt$/i) {
427             # login() always looks for a cmd_prompt. This is not
428             # controllable via always_waitfor_prompt().
429 0           $cmd_prompt = $self->_prompt_append($cmd_prompt, $args{$_});
430             } elsif (/^-?timeout$/i) {
431 0           $timeout = _parse_timeout($args{$_});
432             } else {
433 0           return $self->error($usage);
434             }
435             }
436             }
437              
438 0 0         print "login:\t[after args: $cmd_prompt]\n" if $DEBUG;
439              
440             ## Override these user set-able values.
441 0           $endtime = _endtime($timeout);
442 0           $orig_timeout = $self->timeout($endtime);
443 0           $orig_errmode = $self->errmode;
444              
445             ## Create a subroutine to reset to original values.
446             $reset
447             = sub {
448 0     0     $self->errmode($orig_errmode);
449 0           $self->timeout($orig_timeout);
450 0           1;
451 0           };
452              
453             ## Create a subroutine to generate an error for user.
454             $error
455             = sub {
456 0     0     my($errmsg) = @_;
457              
458 0           &$reset;
459 0 0         if ($self->timed_out) {
    0          
460 0           return $self->error($errmsg);
461             } elsif ($self->eof) {
462 0           ($lastline = $self->lastline) =~ s/\n+//;
463 0           return $self->error($errmsg, ": ", $lastline);
464             } else {
465 0           return $self->error($self->errmsg);
466             }
467 0           };
468              
469              
470             # Send a newline as the wakeup-call
471 0 0         if ($self->send_wakeup eq 'connect') {
472              
473 0           $sent_wakeup = 1;
474              
475 0           my $old_sep = $self->output_record_separator;
476              
477 0           $self->output_record_separator("\n");
478 0           $self->print('');
479 0           $self->output_record_separator($old_sep);
480             }
481              
482              
483 0           while (1) {
484 0           (undef, $_) = $self->waitfor(
485             -match => '/(?:[Ll]ogin|[Uu]sername|[Pp]assw(?:or)?d)[:\s]*$/',
486             -match => '/(?i:Passcode)[:\s]*$/',
487             -match => $cmd_prompt,
488             );
489              
490 0 0         unless ($_) {
491 0 0         return &$error("read eof waiting for login or password prompt")
492             if $self->eof;
493              
494             # We timed-out. Send a newline as the wakeup-call.
495 0 0 0       if ($sent_wakeup == 0 && $self->send_wakeup eq 'timeout') {
496              
497 0           $sent_wakeup = 1;
498              
499 0           my $old_sep = $self->output_record_separator;
500              
501 0           $self->output_record_separator("\n");
502 0           $self->print('');
503 0           $self->output_record_separator($old_sep);
504              
505 0           next;
506             }
507              
508 0           return &$error("timed-out during login process");
509             }
510              
511 0           my $cmd_prompt_re = $self->_re_sans_delims($cmd_prompt);
512              
513 0 0         if (not defined) {
    0          
    0          
    0          
    0          
514 0           return $self->error("login failed: access denied or bad name, passwd, etc");
515             } elsif (/sername|ogin/) {
516 0 0         $self->print($username) or return &$error("login disconnected");
517 0 0         $seen{login}++ && $self->error("login failed: access denied or bad username");
518             } elsif (/[Pp]assw/) {
519 0 0         $self->print($password) or return &$error("login disconnected");
520 0 0         $seen{passwd}++ && $self->error("login failed: access denied or bad password");
521             } elsif (/(?i:Passcode)/) {
522 0 0         $self->print($passcode) or return &$error("login disconnected");
523 0 0         $seen{passcode}++ && $self->error("login failed: access denied or bad passcode");
524             } elsif (/($cmd_prompt_re)/) {
525 0           &$reset; # Success. Reset obj to default vals before continuing.
526 0           last;
527             } else {
528 0           $self->error("login received unexpected prompt. Aborting.");
529             }
530             }
531              
532 0           1;
533             } # end sub login
534              
535              
536             # Overridden to support ignore_warnings()
537             sub error {
538 0     0 1   my $self = shift;
539              
540             # Ignore warnings
541 0 0         if ($self->ignore_warnings) {
542 0           my $errmsg = join '', @_;
543 0           my $warnings_re = $self->_re_sans_delims($self->warnings);
544 0 0         return if $errmsg =~ /$warnings_re/;
545             }
546              
547 0           return $self->SUPER::error(@_);
548             }
549              
550              
551             # Tries to enter enabled mode with the password arg.
552             sub enable {
553 0     0 1   my $self = shift;
554 0           my $usage = 'usage: $obj->enable([Name => $name,] [Password => $password,] '
555             . '[Passcode => $passcode,] [Level => $level] )';
556 0           my ($en_username, $en_password, $en_passcode, $en_level) = ('','','','');
557 0           my ($error, $lastline, $orig_errmode, $reset, %args, %seen);
558              
559 0 0         if (@_ == 1) { # just passwd given
560 0           ($en_password) = shift;
561             } else { # named args given
562 0           %args = @_;
563              
564 0           foreach (keys %args) {
565 0 0         if (/^-?name$|^-?login$|^-?user/i) {
    0          
    0          
    0          
566 0           $en_username = $args{$_};
567             } elsif (/^-?passw/i) {
568 0           $en_password = $args{$_};
569             } elsif (/^-?passc/i) {
570 0           $en_passcode = $args{$_};
571             } elsif (/^-?level$/i) {
572 0           $en_level = $args{$_};
573             } else {
574 0           return $self->error($usage);
575             }
576             }
577             }
578              
579             ## Create a subroutine to generate an error for user.
580             $error = sub {
581 0     0     my($errmsg) = @_;
582              
583 0 0         if ($self->timed_out) {
    0          
584 0           return $self->error($errmsg);
585             } elsif ($self->eof) {
586 0           ($lastline = $self->lastline) =~ s/\n+//;
587 0           return $self->error($errmsg, ": ", $lastline);
588             } else {
589 0           return $self->error($errmsg);
590             }
591 0           };
592              
593             # Store the old prompt without the //s around it.
594 0           my ($old_prompt) = $self->_re_sans_delims($self->prompt);
595              
596             # We need to expect either a Password prompt or a
597             # typical prompt. If the user doesn't have enough
598             # access to run the 'enable' command, the device
599             # won't even query for a password, it will just
600             # ignore the command and display another [boring] prompt.
601 0           $self->print("enable $en_level");
602              
603             {
604 0           my ($prematch, $match) = $self->waitfor(
605             -match => '/[Ll]ogin[:\s]*$/',
606             -match => '/[Uu]sername[:\s]*$/',
607             -match => '/[Pp]assw(?:or)?d[:\s]*$/',
608             -match => '/(?i:Passcode)[:\s]*$/',
609             -match => "/$old_prompt/",
610 0 0         ) or do {
611 0 0         return &$error("read eof waiting for enable login or password prompt")
612             if $self->eof;
613 0           return &$error("timed-out waiting for enable login or password prompt");
614             };
615              
616 0 0         if (not defined $match) {
    0          
    0          
    0          
    0          
617 0           return &$error("enable failed: access denied or bad name, passwd, etc");
618             } elsif ($match =~ /sername|ogin/) {
619 0 0         $self->print($en_username) or return &$error("enable failed");
620 0 0         $seen{login}++
621             && return &$error("enable failed: access denied or bad username");
622 0           redo;
623             } elsif ($match =~ /[Pp]assw/ ) {
624 0 0         $self->print($en_password) or return &$error("enable failed");
625 0 0         $seen{passwd}++
626             && return &$error("enable failed: access denied or bad password");
627 0           redo;
628             } elsif ($match =~ /(?i:Passcode)/ ) {
629 0 0         $self->print($en_passcode) or return &$error("enable failed");
630 0 0         $seen{passcode}++
631             && return &$error("enable failed: access denied or bad passcode");
632 0           redo;
633             } elsif ($match =~ /$old_prompt/) {
634             ## Success! Exit the block.
635 0           last;
636             } else {
637 0           return &$error("enable received unexpected prompt. Aborting.");
638             }
639             }
640              
641 0 0 0       if (($en_level eq '') or ($en_level =~ /^[1-9]/)) {
642             # Prompts and levels over 1 give a #/(enable) prompt.
643 0 0         return $self->is_enabled ? 1 : &$error('Failed to enter enable mode');
644             } else {
645             # Assume success
646 0           return 1;
647             }
648             }
649              
650             # Leave enabled mode.
651             sub disable {
652 0     0 1   my $self = shift;
653 0           $self->cmd('disable');
654 0 0         return $self->is_enabled ? $self->error('Failed to exit enabled mode') : 1;
655             }
656              
657             sub fhopen {
658 0     0 1   my $self = shift;
659 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
660 0           $self->SUPER::fhopen(@_);
661 0           *$self->{net_telnet_cisco} = $stream;
662              
663 0           1;
664             }
665              
666             # Send control-^ (without newline)
667             sub ios_break {
668 0     0 1   my $self = shift;
669              
670 0           my $old_ors = $self->output_record_separator;
671 0           $self->output_record_separator('');
672 0           my $ret = $self->print("\c^");
673 0           $self->output_record_separator($old_ors);
674              
675 0           return $ret;
676             }
677              
678             # Displays the last prompt.
679             sub last_prompt {
680 0     0 1   my $self = shift;
681 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
682 0 0         exists $stream->{last_prompt} ? $stream->{last_prompt} : undef;
683             }
684              
685             # Displays the last command.
686             sub last_cmd {
687 0     0 1   my $self = shift;
688 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
689 0 0         exists $stream->{last_cmd} ? $stream->{last_cmd} : undef;
690             }
691              
692             # Examines the last prompt to determine the current mode.
693             # Some prompts may be hard set to #, so this won't always return a valid answer.
694             # Call 'show priv' instead.
695             # 1 => enabled.
696             # undef => not enabled.
697 0 0   0 1   sub is_enabled { $_[0]->last_prompt =~ /\#|enable|config/ ? 1 : undef }
698              
699             # Typical get/set method.
700             sub always_waitfor_prompt {
701 0     0 1   my ($self, $arg) = @_;
702 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
703 0 0         $stream->{always_waitfor_prompt} = $arg if defined $arg;
704 0           return $stream->{always_waitfor_prompt};
705             }
706              
707             # Typical get/set method.
708             sub waitfor_pause {
709 0     0 1   my ($self, $arg) = @_;
710 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
711 0 0         $stream->{waitfor_pause} = $arg if defined $arg;
712 0           return $stream->{waitfor_pause};
713             }
714              
715             # Typical get/set method.
716             sub autopage {
717 0     0 1   my ($self, $arg) = @_;
718 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
719 0 0         $stream->{autopage} = $arg if defined $arg;
720 0           return $stream->{autopage};
721             }
722              
723             # Typical get/set method.
724             sub normalize_cmd {
725 0     0 1   my ($self, $arg) = @_;
726 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
727 0 0         $stream->{normalize_cmd} = $arg if defined $arg;
728 0           return $stream->{normalize_cmd};
729             }
730              
731             # Typical get/set method.
732             sub send_wakeup {
733 0     0 1   my ($self, $arg) = @_;
734 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
735 0 0         $stream->{send_wakeup} = $arg if defined $arg;
736 0           return $stream->{send_wakeup};
737             }
738              
739             # Typical get/set method.
740             sub ignore_warnings {
741 0     0 1   my ($self, $arg) = @_;
742 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
743 0 0         $stream->{ignore_warnings} = $arg if defined $arg;
744 0           return $stream->{ignore_warnings};
745             }
746              
747             # Get/set the More prompt
748             sub more_prompt {
749 0     0 1   my ($self, $arg) = @_;
750 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
751 0 0         if (defined $arg) {
752 0           $self->_match_check($arg);
753 0           $stream->{more_prompt} = $arg;
754             }
755 0           return $stream->{more_prompt};
756             }
757              
758             sub warnings {
759 0     0 1   my ($self, $arg) = @_;
760 0           my $stream = $ {*$self}{net_telnet_cisco};
  0            
761 0 0         if (defined $arg) {
762 0           $self->_match_check($arg);
763 0           $stream->{warnings} = $arg;
764             }
765 0           return $stream->{warnings};
766             }
767              
768             #------------------------------
769             # Private methods
770             #------------------------------
771              
772             # Join two or more regexen into one on "|".
773             sub _prompt_append {
774 0     0     my $self = shift;
775 0   0       my $orig = shift || '';
776 0 0         return $self->error("usage: \$obj->_prompt_append(orig, new, [new...])")
777             unless @_;
778              
779 0 0         print "_prompt_append:\t[original: $orig]\n" if $DEBUG;
780              
781 0 0         if ($orig) {
782 0 0         if ($self->_match_check($orig)) {
783 0           $orig = $self->_re_sans_delims($orig);
784 0 0         return $self->error("Can't parse prompt: '$orig'") unless $orig;
785             }
786             }
787              
788 0           for (@_) {
789 0 0         print "_prompt_append:\t[append: $_]\n" if $DEBUG;
790 0 0         if ($self->_match_check($_)) {
791 0           my $re = $self->_re_sans_delims($_);
792              
793 0 0         unless ($re) {
794 0           $self->error("Can't parse prompt: '$_'");
795 0           next;
796             }
797              
798 0 0         $orig .= $orig ? "|$re" : $re;
799             }
800             }
801              
802 0 0         print "_prompt_append:\t[return: /$orig/]\n\n" if $DEBUG;
803 0           return "/$orig/";
804             }
805              
806             # Return a Net::Telnet regular expression without the delimiters.
807             sub _re_sans_delims {
808 0     0     my ($self, $str) = @_;
809              
810 0 0         return $self->error("usage: \$obj->_re_sans_delims(\$matchop)")
811             unless $str;
812              
813 0           $self->_match_check($str);
814 0           my ($delim, $re) = $str =~ /^\s*m?\s*(\W)(.*)\1\s*$/;
815 0           return $re;
816             }
817              
818             # strip backspaces, deletes, kills, and the character they
819             # pertain to, from an array.
820             sub _normalize {
821 0     0     $_ = join "", @_;
822              
823 0           1 while s/[^\cH\c?][\cH\c?]//mg; # ^H ^?
824 0           s/^.*\cU//mg; # ^U
825              
826 0 0         return wantarray ? split /^/, $_ : $_; # ORS instead?
827             }
828              
829             # Lifted from Net::Telnet en toto
830             sub _match_check {
831 0     0     my ($self, $code) = @_;
832 0 0         return unless $code;
833              
834 0           my $error;
835 0           my @warns = ();
836              
837 0 0         print "_match_check:\t[Checking: $code]\n" if $DEBUG;
838              
839             ## Use eval to check for syntax errors or warnings.
840             {
841 0           local $SIG{'__DIE__'} = 'DEFAULT';
  0            
842 0     0     local $SIG{'__WARN__'} = sub { push @warns, @_ };
  0            
843 0           local $^W = 1;
844 0           local $_ = '';
845 0           eval "\$_ =~ $code;";
846             }
847 0 0         if ($@) {
    0          
848             ## Remove useless lines numbers from message.
849 0           ($error = $@) =~ s/ at \(eval \d+\) line \d+.?//;
850 0           chomp $error;
851 0           return $self->error("bad match operator: $error");
852             }
853             elsif (@warns) {
854             ## Remove useless lines numbers from message.
855 0           ($error = shift @warns) =~ s/ at \(eval \d+\) line \d+.?//;
856 0           $error =~ s/ while "strict subs" in use//;
857 0           chomp $error;
858 0           return $self->error("bad match operator: $error");
859             }
860              
861 0           1;
862             } # end sub _match_check
863              
864             #------------------------------
865             # Class methods
866             #------------------------------
867              
868             # Look for subroutines in Net::Telnet if we can't find them here.
869             sub AUTOLOAD {
870 0     0     my ($self) = @_;
871 0 0         croak "$self is an [unexpected] object, aborting" if ref $self;
872 0           $AUTOLOAD =~ s/.*::/Net::Telnet::/;
873 0           goto &$AUTOLOAD;
874             }
875              
876             1;
877              
878             __END__