martes, 24 de febrero de 2009

Agis + php + GotoIfTime + mysql + asterisk = Un par de horas de trabajo.

Bueno...
Primero que todo "hola", ya mucho saludo...

El motivo de esta entrada es ayudar a los Sysadmin que tienen bajo responsabilidad servidores asterisk.
Con el fin de que la telefonia, no se transforme en un abuso despues del horario laboral.

Caso hipotetico:
- Supongamos que el horario laboral de una empresa es entre las 09 y 19 hrs entre lunes a viernes,
una vez que todo el personal se halla retirado de las dependencias de su empresa, llega la persona encargada de realizar el aseo a esta. A lo que vamos, es que nadie nos asegura que esta persona, no se pondra a realizar largas llamadas con coste para la empresa.

Entonces, nos quedan 3 opciones...
- Limitamos para que nadie realice llamadas.
- Dejamos el sistema abierto y que llame descontroladamente.
- Generamos un sistema de autentificación el cual funcionara despues de la jornada laboral.

Obviamente descartaremos las primeras 2 opciones.
En la tercera opción, la podemos realizar de 2 formas...
Una con authenticate y disa, las cuales me dio flojera adentrarme en el tema...
y la otra con Agi y bases de datos...

Obviamente, por el titulo de esta entrada, ya sabran cual usaremos esta vez.
ya...
manos a la obra...
lo primero que necesitaremos es:
Un servidor corriendo con asterisk, lo ideal que la distribución sea Debian en el caso de linux.
Ya que Redhat y CentOS apéstan.

Necesitaremos:
php5
php5-mysql
php5-cli
mysql-server
y amigos (o sea... dependencias).
phpagi v2.

Una vez que tengan todo esto instalado,
Procederemos a la configuración.

Todos las AGI de asterisk, por defecto se encuentran en el directorio "/var/lib/asterisk/agi-bin"
Las AGI son como los cgi del http, esto quiere decir que a nuestra central le haremos ejecutar un archivo, al cual le pasaremos parametros de asterisk para que ejecute de mejor forma el proceso.

descomprimir el archivo phpagi-2.14.tar.gz (este es el usado en este ejemplo, aunque la version puede variar) dentro del directorio "/var/lib/asterisk/agi-bin", lo cual nos dara un directorio llamado "phpagi-2.14/", renombrenlo como quieran, es cosa de cada uno...

El tema es que en mi caso particular lo deje tal cual.:

pbx:~# ls /var/lib/asterisk/agi-bin/phpagi-2.14
COPYING docs mkdocs.php phpagi-asmanager.php phpagi-fastagi.php phpagi.php README

ya...
ahora procederemos a crear una nueva entrada en el dialplan (extensions.conf).

Con su editor de texto favorito editen el archivo...
en mi caso estoy acostumbrado a "vi".
############################################

Asterisk

############################################
pbx:~# vi /etc/asterisk/extensions.conf #### y agregamos lo siguiente.

[locales_prueba]
exten => _XXXXXXX,1,GotoIfTime(9:00-19:00|mon-fri|*|*?dentrohorario,${EXTEN},1)
exten => _XXXXXXX,n,Answer
exten => _XXXXXXX,n,Agi(logea|${EXTEN}|${CALLERID(NUM)})
exten => _XXXXXXX,n,hangup

[dentrohorario]
exten => _XXXXXXX,1,Dial(Zap/g1/${EXTEN},50)
exten => _XXXXXXX,n,hangup

######### EOF

la primera entrada es la que le definiremos a los usuarios SIP como contexto.
En la primera prioridad del contexto vemos algo como GotoIfTime, hare una explicación corta, ya que me cargan las explicaciones largas...
Quiere decir que haga un salto a otro contexto u otra prioridad si se cumple la condición del horario.
es decir... que si se encuentra dentro del horario, entonces salte al contexto [dentrohorario] que ejecute la primera prioridad de la llamada...
me entienden?...
un poco mas simple....

si realizan una llamada dentro del horario al telefono 1111111, entonces esta le pasa la llamada al contexto [dentrohorario] y la prioridad 1, que esta vendria ejecutando la llamada.
Si esto no fuera asi, entonces ejecutaria la prioridad 2 dentro del contexto [locales_prueba], la cual lo que hace es ejecutar el script AGI llamado logea, con sus respectivos parametros.

${EXTEN} = "Telefono de destino, o sea... a que numero se esta llamando (1111111)".
${CALLERID(NUM)} = "Quien es el que realiza la llamada".

################################

phpagi

################################

Una vez destarjetezeado el archivo (que chileno), ingresamos a el...
pbx:~# cd /var/lib/asterisk/agi-bin/phpagi-2.14/
pbx:~# ls
CHANGELOG CVS fastagi.xinetd phpagi.example.conf README.phpagi README.phpagi-asmanager README.phpagi-fastagi

Aca lo unico que nos interesa es el archivo phpagi.example.conf, el cual copiaremos a /etc/asterisk/phpagi.conf

posterior a ello, editamos el archivo:

pbx:~# vi /etc/asterisk/phpagi.conf
Lo unico que editamos es la seccion [phpagi] y modificamos los parametros.
[phpagi]
debug=true
error_handler=true
admin=tu@correo
hostname=el_host_de_la_maquina_de_asterisk
tempdir=/var/spool/asterisk/tmp/

############ EOF ################

Ahora procederemos a configurar nuestros scripts.
Primero debemos crear una base de datos para los usuarios y las claves.

pbx:~# mysql asterisk
mysql> create table usuario ( id_usuario int(10) not null auto_increment, nombre_usuario char(100) not null default'', clave_usuario char(64) not null default'', primary key(id_usuario));

#### Donde nombre_usuario contendra el numero que realiza la llamada, enrealidad deberia llamarse numero_usuario, pero ahi lo modifican ustedes, ya que esto es un mero y funcional ejemplo del mini sistema.

Una vez generada la tabla para los usuarios, le agregamos uno de prueba...
insert into usuario values (0,'123','e10adc3949ba59abbe56e057f20f883e');

La clave del usuario es "123456" (sin comillas), esto es sumamente facil con php y la funcion md5
resumido...

hagan un script php y pongan lo siguiente:

vi archivo.php e ingresan esto a continuación.

#!/usr/bin/php5 -q
<?php
$string="123456";
$clave=md5($string);
printf("$clave\n");
?>

pbx:~# chmod +x archivo.php
pbx:~# ./archivo.php
e10adc3949ba59abbe56e057f20f883e
pbx:~#

ahora bien...
Necesitamos crear el archivo "logeo" el cual contendra la logica que procesara los nombres de usuarios y a su vez les solicitara la clave correspondiente.

#!/usr/bin/php5 -q
<?php
$org=$argv[2];
$dst=$argv[1];
require ("phpagi-2.14/phpagi.php");
error_reporting(E_ALL);
$agi = new AGI();
$agi->answer();
$agi->text2wav("Porfavor ingrese su clave");
$recoje=$agi->get_data('beep',3000,10);
$clavex=$recoje['result'];
if(!empty($clavex)){
$host="localhost";
$user="USUARIO";
$pass="PASSWORD";
$db="asterisk";
$con=mysql_connect($host,$user,$pass) or die(mysql_error());
mysql_select_db($db,$con);
$clave=md5($clavex);
$sql=mysql_query("select * from usuario where nombre_usuario='$org' and clave_usuario='$clave'",$con);
$rs=mysql_fetch_array($sql);
$logeado=$rs['nombre_usuario'];
if(empty($logeado)){
$agi->text2wav("La clave ingresada es incorrecta Adios");
} else {
$agi->exec("Dial Zap/g1/$dst,50");
}
mysql_close($con);
} else {
$agi->text2wav("El tiempo de espera ha concluido Adios");
}
$agi->Hangup();
?>

############
Aca hay un tema que no se ha tocado, y que es el que el lector actualemente se haya dado cuenta...
es sobre "text2wav", creo que con solo leer sabran para que sirve...
Para esto necesitamos el programa festival el cual pasa de texto a voz, pero sinceramente las voces por defecto son demasiado roboticas, asi que les aconsejaria que siguieran el siguiente "howto" el cual explica como instalarlo y dejarlo funcional:

festival en spanish (Coño!)
############

Ahora bien...
Para que los mismos usuarios realicen sus propios cambios de claves, sin necesidad que nos esten molestando, o por solamente dejar esto mas transparente, realizamos el siguiente AGI (cambiarclave).
Que a continuación se muestra.:

#!/usr/bin/php5 -q
<?php
require("phpagi-2.14/phpagi.php");
$host="localhost";
$user="USUARIO";
$pass="PASSWORD";
$db="asterisk";
$con=mysql_connect($host,$user,$pass) or die(mysql_error());
mysql_select_db($db,$con);
error_reporting(E_ALL);
$org=$argv[1];
$agi=new AGI();
$agi->answer();
$agi->text2wav("Por favor ingrese su actual clave");
$datos=$agi->get_data('beep',3000,10);
$clave=$datos['result'];
if(!empty($clave)){
$clave=md5($clave);
$sql=mysql_query("select clave_usuario from usuario where nombre_usuario='$org'",$con);
$res=mysql_fetch_array($sql);
$cl=$res['clave_usuario'];
do {
$agi->text2wav("La clave ingresada es incorrecta");
$agi->text2wav("Por favor ingrese su actual clave");
$datos=$agi->get_data('beep',3000,10);
$clave=$datos['result'];
$clave=md5($clave);
} while($cl!=$clave);
$agi->text2wav("Por favor ingrese su nueva clave");
$actualiza1=$agi->get_data('beep',3000,15);
$nueva=$actualiza1['result'];
$agi->text2wav("Reingrese la nueva clave");
$actualiza2=$agi->get_data('beep',3000,15);
$nclave=$actualiza2['result'];
do {
$agi->text2wav("Las claves no coinciden");
$agi->text2wav("Por favor ingrese su nueva clave");
$actualiza1=$agi->get_data('beep',3000,15);
$nueva=$actualiza1['result'];
$agi->text2wav("Reingrese la nueva clave");
$actualiza2=$agi->get_data('beep',3000,15);
$nclave=$actualiza2['result'];
} while($nclave!=$nueva);
if($nclave==$nueva){
$clave_nueva=md5($nclave);
$update="update usuario set clave_usuario='$clave_nueva' where nombre_usuario='$org'";
mysql_query($update,$con);
$agi->text2wav("La clave ha sido cambiada exitosamente su nueva clave es $nclave");
$agi->text2wav("adios");
$agi->hangup();
}
mysql_close($con);
} else {
$agi->text2wav("El tiempo limite ha sido excedido adios");
$agi->hangup();
}
?>

Para habilitar este servicio.
debemos generar una nueva entrada al contexto de los usuarios, por ejemplo designemosles el numero "222", la sintaxis seria la siguiente:

exten => 222,1,Answer
exten => 222,n,Agi(cambiarclave|${CALLERID(NUM)})
exten => 222,n,Hangup

Dentro del contexto definido para los usuarios, que no se les olvide...
En este caso ponganlo dentro locales_prueba y dentrohorario.

Solo nos queda darles permiso de ejecución a los archivos.
esto seria:

pbx:~# cd /var/lib/asterisk/agi-bin
pbx:/var/lib/asterisk/agi-bin# chmod +x logeo cambiarclave

y Realizamos las pruebas correspondiente...

Espero que les sirva, y he intentado no dejar nada en el tintero...
si falta algo o no funciona, posteenlo y lo respondere a la brevedad...

Dedicatoria:

- A mi compare Poncio Monsalvez (talla interna).
- A mi PC que le cayo cafe en el teclado el dia de hoy, pero ni se quejo.
- Al Juan por hablar estupideces...
- Al Poncio que me presiona para que le envie los tips.

Eso seria...

Fuentes:
http://phylevn.mexrom.net/index.php/blog/show/Como_implementar_un_AGI_con_PHP_en_un_conmutador_de_VoIP_sobre_Asterisk.html
http://google.cl
http://www.voip-info.org