use Expect; use Data::Dumper; use Term::VT102; ## this is a script to extract data from Avaya Communication Manager ## more info at http://rogerthephoneguy.com/?p=421 ## Tweaked from code posted at http://tools.cac.washington.edu/2010/04/avaya-pbx-admin-web-service.html ## let me know how it works for you! Roger Anderson, roger@rogerthephoneguy.com my $DEBUG = 0; use constant TERMTYPE => 'ossi4'; # options are ossi, ossi3, or ossi4 use constant TIMEOUT => 10; sub trim($) { my $string = shift; $string =~ s/^\s+//; $string =~ s/\s+$//; return $string; } sub login { my ($host, $port, $username, $password, $connection_type) = @_; my $success = 0; my $s = new Expect; $s->log_stdout(0); $s->raw_pty(1); $s->restart_timeout_upon_receive(1); my $command; if ( $connection_type eq 'telnet' ) { $command = "$telnet_command $host $port"; } elsif ( $connection_type eq 'ssh' ) { $command = "ssh -o \"StrictHostKeyChecking no\" -p $port -l $username $host"; } else { my $msg = "ERROR: unhandled connection type requested. [$connection_type]"; print "$msg\n" if $DEBUG; return(0); } print "$command\n" if $DEBUG; $s->spawn($command); if (defined($s)){ $success = 0; $s->expect(TIMEOUT, [ 'Login resources unavailable', sub { my $msg = "ERROR: No ports available."; print "$msg\n" if $DEBUG; }], [ '-re', '[Ll]ogin:|[Uu]sername:', sub { my $self = shift; print "Login: $username\n" if $DEBUG; print $self "$username\r"; exp_continue; }], [ 'Password:', sub { my $self = shift; print "entering password\n" if $DEBUG; print $self "$password\r"; exp_continue; }], [ 'Terminal Type', sub { my $self = shift; print "entering terminal type ".TERMTYPE."\n" if $DEBUG; print $self TERMTYPE . "\r"; exp_continue; }], [ '-re', '^t$', sub { print "connection established\n" if $DEBUG; $success = 1; }], [ eof => sub { my $msg = "ERROR: Connection failed with EOF at login."; print "$msg\n" if $DEBUG; }], [ timeout => sub { my $msg = "ERROR: Timeout on login."; print "$msg\n" if $DEBUG; }] ); if (! $success) { return(0); } else { # Verify command prompt ... sleep(1); print $s "\rt\r"; $s->expect(TIMEOUT, [ '-re', 'Terminator received but no command active\nt\012'], [ eof => sub { $success = 0; my $msg = "ERROR: Connection failed with EOF at verify command prompt."; print "$msg\n" if $DEBUG; }], [ timeout => sub { $success = 0; my $msg = "ERROR: Timeout on verify command prompt."; print "$msg\n" if $DEBUG; }], [ '-re', '^t$', sub { exp_continue; }] ); if (! $success) { return(0); } } } else { my $msg = "ERROR: Could not create an Expect object."; print "$msg\n" if $DEBUG; } return($s); } sub logoff { my ($session) = @_; if ( $session ) { $session->send("c logoff \rt\r"); $session->expect(TIMEOUT, [ qr/NO CARRIER/i ], [ qr/Proceed With Logoff/i, sub { my $self = shift; $self->send("y\r"); } ], [ qr/onnection closed/i ] ); $session->soft_close(); print "PBX connection disconnected\n" if $DEBUG; } return(0); } sub pbx_command { my ($session, $command, %fields) = @_; my $ossi_output = {}; my $this = $self; my @field_ids; my @field_values; my $cmd_fields = ''; my $cmd_values = ''; my $command_succeeded = 1; my @ossi_objects = (); my $errormsg = ''; print "DEBUG Processing pbx_command($command, \%fields)\n" if $DEBUG; print "DEBUG \%fields contains:\n" if $DEBUG; print Dumper(%fields) if $DEBUG; for my $field ( sort keys %fields ) { my $value = $fields{$field}; $cmd_fields .= "$field\t"; $cmd_values .= "$value\t"; } chop $cmd_fields; # remove the trailing \t character chop $cmd_values; $session->send("c $command\r"); print "DEBUG Sending \nc $command\n" if $DEBUG; if ( $cmd_fields ne '' ) { $session->send("f$cmd_fields\r"); print "f$cmd_fields\n" if $DEBUG; $session->send("d$cmd_values\r"); print "d$cmd_values\n" if $DEBUG; } $session->send("t\r"); print "t\n" if $DEBUG; $session->expect(TIMEOUT, [ '-re', '^f.*\x0a', sub { my $self = shift; my $a = trim( $self->match() ); print "DEBUG Matched '$a'\n" if $DEBUG; $a =~ s/^f//; # strip the leading 'f' off my ($field_1, $field_2, $field_3, $field_4, $field_5) = split(/\t/, $a, 5); print "field_ids are: $field_1|$field_2|$field_3|$field_4|$field_5\n" if ($DEBUG); push(@field_ids, $field_1); push(@field_ids, $field_2); push(@field_ids, $field_3); push(@field_ids, $field_4); push(@field_ids, $field_5); exp_continue; } ], [ '-re', '^[dent].*\x0a', sub { my $self = shift; my $a = trim( $self->match() ); print "DEBUG Matched '$a'\n" if $DEBUG; if ( trim($a) eq "n" || trim($a) eq "t" ) { # end of record output # assign values to $ossi_output object for (my $i = 0; $i < scalar(@field_ids); $i++) { if ( $field_ids[$i] ) { $ossi_output->{$field_ids[$i]} = $field_values[$i]; } } # print Dumper($ossi_output) if $DEBUG; delete $ossi_output->{''}; # I'm not sure how this get's added but we don't want it. push(@ossi_objects, $ossi_output); @field_values = (); undef $ossi_output; } elsif ( substr($a,0,1) eq "d" ) { # field data line $a =~ s/^d//; # strip the leading 'd' off my ($field_1, $field_2, $field_3, $field_4, $field_5) = split(/\t/, $a, 5); print "field_values are: $field_1|$field_2|$field_3|$field_4|$field_5\n" if ($DEBUG); push(@field_values, $field_1); push(@field_values, $field_2); push(@field_values, $field_3); push(@field_values, $field_4); push(@field_values, $field_5); } elsif ( substr($a,0,1) eq "e" ) { # error message line $a =~ s/^e//; # strip the leading 'd' off my ($field_1, $field_2, $field_3, $field_4) = split(/ /, $a, 4); my $mess = $field_4; print "ERROR: field $field_2 $mess\n" if $DEBUG; $errormsg .= "$field_2 $mess\n"; $command_succeeded = 0; } else { print "ERROR: unknown match \"" . $self->match() ."\"\n"; } unless ( trim($a) eq "t" ) { exp_continue; } } ], [ eof => sub { $command_succeeded = 0; my $msg = "ERROR: Connection failed with EOF in pbx_command($command)."; print "$msg\n" if $DEBUG; $errormsg .= $msg; } ], [ timeout => sub { $command_succeeded = 0; my $msg = "ERROR: Timeout in pbx_command($command)."; print "$msg\n" if $DEBUG; $errormsg .= $msg; } ], ); if ( $command_succeeded ) { return @field_values; } else { print "failed with $errormsg\n"; return(0); } } sub pbx_vt220_command { my ($session, $command) = @_; my $command_succeeded = 1; my $errormsg = ''; $vt220_output = ''; @vt220_screens = (); my $command_output = ''; my $ESC = chr(27); # \x1b my $CANCEL = $ESC . "[3~"; my $NEXT = $ESC . "[6~"; #4410 keys #F1=Cancel=OP #F2=Refresh=OQ #F3=Save=OR #F4=Clear=OS #F5=Help=OT #F6=GoTo=Or ...OR... F6=Update=OX ...or.... F6=Edit=f6 #F7=NextPg=OV #F8=PrevPg=OW #VT220 keys #Cancel ESC[3~ F1 #Refresh ESC[34~ F2 #Execute ESC[29~ F3 #Clear Field ESC[33~ F4 #Help ESC[28~ F5 #Update Form ESC[1~ F6 #Next Page ESC[6~ F7 #Previous Page ESC[5~ F8 # switch the terminal type from ossi to VT220 $session->send("c newterm\rt\r"); print "DEBUG switching to VT220 terminal type\n" if $DEBUG; $session->expect(TIMEOUT, [ 'Terminal Type', sub { $session->send("VT220\r"); print "DEBUG sending VT220\n" if $DEBUG; exp_continue; }], [ '-re', 'Command:', sub { print "DEBUG ready for next command.\n" if $DEBUG; }], [ timeout => sub { my $msg = "ERROR: Timeout switching to VT220 terminal type."; print "$msg\n" if $DEBUG; $errormsg .= $msg; }] ); $session->send("$command\r"); print "DEBUG Sending $command\n" if $DEBUG; $session->expect(TIMEOUT, [ '-re', '\x1b\[\d;\d\dH\x1b\[0m|\[KCommand:|press CANCEL to quit -- press NEXT PAGE to continue|Command successfully completed', sub { # end of screen #\[24;1H\x1b\[KCommand: my $string = $session->before(); $string =~ s/\x1b/\n/gm; print "DEBUG \$session->before()\n$string\n" if $DEBUG; #my $string = $session->before(); #$string =~ s/\x1b/\n/gm; #print "Expect end of page\n$string\n"; my $a = trim( $session->match() ); print "DEBUG \$session->match() '$a'\n" if $DEBUG; my $current_page = 0; my $page_count = 1; if ( $session->before() =~ /Page +(\d*) of +(\d*)/ ) { $current_page = $1; $page_count = $2; } print "DEBUG on page $current_page out of $page_count pages\n" if $DEBUG; my $vt = Term::VT102->new('cols' => 80, 'rows' => 24); $vt->process( $session->before() ); my $row = 0; my $screen; while ( $row < $vt->rows() ) { my $line = $vt->row_plaintext($row); $screen .= "$line\n" if $line; $row++; } print $screen if $DEBUG; #push( @{$self->{'VT220_SCREENS'}}, $screen); push(@vt220_screens,$screen); $command_output .= $screen; if ( $session->match() eq 'Command successfully completed') { print "DEBUG \$session->match() is 'Command successfully completed'\n" if $DEBUG; } elsif ( $session->match() eq '[KCommand:') { print "DEBUG returned to 'Command:' prompt\n" if $DEBUG; if ( $session->after() ne ' ' ) { print "DEBUG \$session->after(): '". $session->after() ."'" if $DEBUG; my $vt = Term::VT102->new('cols' => 80, 'rows' => 24); $vt->process( $session->before() ); my $msg = "ERROR: ". $vt->row_plaintext(23); print "$msg\n" if $DEBUG; $errormsg .= $msg; $session->send("$CANCEL"); $command_succeeded = 0; } } elsif ($current_page == $page_count) { print "DEBUG received last page. command finished\n" if $DEBUG; $session->send("$CANCEL"); } elsif ($current_page < $page_count ) { print "DEBUG requesting next page\n" if $DEBUG; $session->send("$NEXT"); exp_continue; } else { print "ERROR: unknown condition\n" if $DEBUG; } }], [ eof => sub { $command_succeeded = 0; my $msg = "ERROR: Connection failed with EOF in pbx_vt220_command($command)."; print "$msg\n" if $DEBUG; $errormsg .= $msg; } ], [ timeout => sub { $command_succeeded = 0; my $string = $session->before(); $string =~ s/\x1b/\n/gm; print "ERROR: timeout in pbx_vt220_command($command)\n\$session->before()\n$string\n" if $DEBUG; my $vt = Term::VT102->new('cols' => 80, 'rows' => 24); $vt->process( $session->before() ); my $msg = "ERROR: ". $vt->row_plaintext(23); print "$msg\n" if $DEBUG; $errormsg .= $msg; $session->send("$CANCEL"); } ], ); # switch back to the original ossi terminal type print "DEBUG switching back to ossi terminal type\n" if $DEBUG; $session->send("$CANCEL"); $session->send("newterm\r"); print "DEBUG sending cancel and newterm\n" if $DEBUG; $session->expect(TIMEOUT, [ 'Terminal Type', sub { $session->send(TERMTYPE . "\r"); print "DEBUG sending ". TERMTYPE ."\n" if $DEBUG; exp_continue; }], [ '-re', '^t$', sub { print "DEBUG ready for next command\n" if $DEBUG; }], [ timeout => sub { my $msg = "ERROR: Timeout while switching back to ossi terminal."; print "$msg\n" if $DEBUG; $errormsg .= $msg; }] ); if ( $command_succeeded ) { #$self->{'LAST_COMMAND_SUCCEEDED'} = 1; #$self->{'VT220_OUTPUT'} = $command_output; $vt220_output = $command_output; print "DEBUG command succeeded\n" if $DEBUG; return $vt220_output; } else { $self->{'LAST_COMMAND_SUCCEEDED'} = 0; print "DEBUG command failed\n" if $DEBUG; return(0); } } print "logging into $ARGV[0]\n"; my $creds = `cat $ARGV[0]`; chomp($creds); my ($host, $port, $user, $pass, $method) = split(/, */,$creds); my $sess = login($host, $port, $user, $pass, $method); if($sess) { open INPUT,$ARGV[1]; while ($line = ) { chomp($line); if($line ne '') { print pbx_vt220_command($sess,$line); } } close INPUT; } logoff($sess);