#!/usr/local/bin/perl

# set volume on Cisco 7940G IP Phones
# script assumes that the extension of the phone is also
# the fourth octet of the ip address.
# Copyright 2010 Jeremy Kister
# Released under Perl's Artistic License
# v2010081701 

# Usage:
# -d debug
# -s silent, but slow
# -x xtns
# -r ringtone
# -v volume

# -x can take multiple extensions in the form of
# SIP/141&SIP/140&SIP/111 or 101&102&103&104

# if you have custom ring tones on the 7940G, the first
# custom ringtone starts at 3

use strict;
use Net::Telnet;
use Getopt::Std;
use Time::HiRes qw(sleep);
use File::TinyLock;

my %opt;
getopts('dsx:r:v:', \%opt);

my $NET = '10.9.1'; # the first three octets of your subnet
my $PW  = 'cisco';  # the password to telnet into your phone

die "cannot use both -v and -r\n" if($opt{v} && $opt{r});
if(defined($opt{r})){
	die "invalid -r" unless($opt{r} eq int($opt{r}));
}elsif(defined($opt{v})){
	die "invalid -v" unless($opt{v} eq int($opt{v}));
}else{
	die "must specify -r or -v\n";
}
my $who = ( $opt{r} + 2 );

my @children;

for my $xtn (split /\&/, $opt{x}){
	next if($xtn =~ /\@/);
	$xtn =~ s/^SIP\///;
	next if($xtn =~ /^19/);
	
	my $pid = fork();
	if($pid){
		push @children, $pid;
	}elsif($pid == 0){
		my $lock = "/tmp/ringer.$xtn";
		my $locksmith = File::TinyLock->new(lock => $lock, retries => 0);
		unless( $locksmith->lock() ){
			die "could not get lock on $xtn\n";
		}
		my $addr = "$net.$xtn";
		my %log;
		if($opt{d}){
			my($y,$m,$d,$min,$sec) = ( localtime(time) )[5,4,3,2,1,0];
			$y += 1900; $m++;
			$m = sprintf('%02d', $m);
			$d = sprintf('%02d', $d);
			$min = sprintf('%02d', $min);
			$sec = sprintf('%02d', $sec);
			my $file = "/tmp/ringer.$y$m$d.$min:$sec.$xtn";
			%log = ( dump   => "$file.dump",
			         input  => "$file.input",
			         output => "$file.output",
			       );
		}
		my $t = Net::Telnet->new(Timeout    => 2,
		                         Dump_log   => $log{dump},
		                         Input_log  => $log{input},
			                 Output_log => $log{output} );
		unless( $t->open($addr) ){
			warn "[$$] couldnt telnet to $addr\n";
			exit;
		}
		
		$t->waitfor('/Password :.*$/');
		$t->print($PW);
		
		my @a = ('open', 'offhook', 'onhook');
		push @a, ('key set', 'key 2') if($opt{r});
		
		if($opt{v}){
			push @a, map { 'key voldn' } (1 .. 30);
			push @a, map { 'key volup' } (1 .. $opt{v});
		}elsif($opt{s}){
			my $down = ($who - 1);
			push @a, map { 'key navdn' } (1 .. $down);
		}else{
			while($who > 10){
				push @a, map { 'key navdn' } (1 .. 10);
				$who -= 10;
			}
			push @a, "key $who";
		}
		push @a, ('key soft1', 'key soft3', 'key set') if($opt{r});
		push @a, 'close';
		
		my $sleep = $opt{r} ? '0.30' : '0.05';
		foreach my $key (@a){
			debug( $key );
		        $t->waitfor('/SIP Phone>\s+$/');
		        $t->print("test ".$key);
			sleep($sleep);
			sleep($sleep) if($key eq 'key soft3' || $key eq 'key set');
		}
		
		$t->waitfor('/SIP Phone>\s+$/');
		$t->print("exit");
	
		$locksmith->unlock();
		exit;
	}else{
		warn "couldnt fork: $!\n";
	}
}

foreach(@children){
	waitpid($_,0);
}

sub debug {
	my ($msg) = join(' ', @_);

	warn "$msg\n" if($opt{d});
}
