Beiträge vom October, 2007

Originar llamadas en Asterisk

Monday, 8. October 2007 14:15

Somos muy cómodos. Para que vamos a engañarnos. Mi jefe dice que no es cuestión de comodidad, sino de productividad. Para qué vamos a tener que marcar un número de un cliente, si tenemos una preciosa base de datos con ellos. Así que me puse manos a la obra y fruto de investigar un poco, salió una pequeña "perla" para llamar desde nuestros teléfonos al cliente con solo hacer un click…

Para que el invento funcione necesitamos:

  1. Un servidor web
  2. Asterisk
  3. Perl

Para el artículo vamos a asumir que tenemos todo esto instalado en nuestra propia máquina (localhost). Empezamos por la configuración del asterisk. El script se conecta al manager de la centralita para lanzar los comandos (no la consola). Así que, lo primero que hay que hacer, es configurarlo. Nos vamos al fichero /etc/asterisk/manager.conf y lo dejamos así:

[general]
enabled = yes
port = 5038
bindaddr = 127.0.0.1

[originate]
read = call
write = call
permit = 127.0.0.1
secret = originatepass

Con esto hemos abierto la interfaz de administración de Asterisk y hemos creado un usuario originate con contraseña originatepass que tiene derechos para lanzar comandos del nivel call. Para más información sobre este tema, podéis pasaros por la sección Asterisk Manager Api de la excelente web voip-info.org

.La segunda parte del invento consiste básicamente en publicar el script que os pondré a continuación, a través de nuestro servidor web. Yo he usado apache así que os pongo cómo sería y dejo a cada cual buscarse el método que más le convenga:

ScriptAlias /asterisk /etc/asterisk/scripts

AllowOverride None
Options ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from 127.0.0.1

Lo he limitado a la propia máquina aunque podéis modificar la directiva Allow para que solo se pueda acceder desde vuestra red local o quitarla directamente. Es importante que el servidor pueda acceder al directorio /etc/asterisk/scripts y que el script tenga permisos de ejecución, o veremos un bonito error 500.

Por último os dejo en este enlace el script originate.pl y además, os lo publico aquí para que le echéis un ojo directamente:

#!/usr/bin/perl -w
# Originate call from command line or cgi script
# Iñaki Rodriguez (2007)
# License: GPL

use IO::Socket::INET;
use strict;
use CGI;

my ($is_cgi,$cgi);
my ($channel,$local_ext,$data_ext,$destination_ext);
my $buffer;
my $debug = 1;

# Connection Data
my $host        = 'localhost';
my $timeout     = 5;
my $username    = 'originate';
my $secret      = 'originatepass';

if(!$ENV{'REMOTE_ADDR'}) {
        $is_cgi = 0;
        if ($ARGV[0] && $ARGV[1]) {
                $local_ext = int($ARGV[0]); # Origin Number
                $destination_ext = int($ARGV[1]); # Destination Number
        } else {
                print "Usage: ./originate.pl  \n";
                exit 1;
        }
} else {
        $is_cgi = 1;
        $cgi = new CGI;
        print $cgi->header();

        if($cgi->param('orig') && $cgi->param('dest')) {
                $local_ext = int($cgi->param('orig')); # Origin Number
                $destination_ext = int($cgi->param('dest')); # Destinatio Number
        } else {
                exit 1;
        }
}

if(!$local_ext || !$destination_ext) {
        print "Invalid values!!!\n";
        exit 1;
}

# Manager Connection
print "Connecting...\n" if $debug;

my $manager_sock = new IO::Socket::INET (PeerPort       => 5038,
                                         Proto          => 'tcp',
                                         PeerAddr       => $host,
                                         Timeout        => $timeout);

die ("Cannot connect to asterisk manager") if (!$manager_sock);
$manager_sock->recv($buffer,4096);

# Send Login Cmd
if(
$manager_sock->send("Action: Login\r\n".
                    "Username: $username\r\n"."Secret: $secret\r\n".
                    "ActionID: 1\r\n\r\n") == 0)
{
        print "Failed to send login information\n";
        exit(1);
}

$manager_sock->recv($buffer,4096);
print "Auth...\n" if $debug;

if($buffer =~ /^Response: Error/) {
        print "Failed to authenticate using $username and $secret\n";
        exit(1);
} 

# Send Originate Cmd
my $originate = "Action: Originate\r\n";
$originate .= "Channel: Local/$local_ext\\@default\r\n";
#$originate .= "Context: default\r\n";
$originate .= "Exten: $destination_ext\r\n";
$originate .= "Priority: 1\r\n\r\n";

# Enviamos y cerramos
$manager_sock->send($originate);

print "Calling...\n" if $debug;
$manager_sock->recv($buffer,4096);
print $buffer if $debug;
$manager_sock->close();

Antes de acabar, os comento un poco por encima el script. Debajo del comentario #Connection Data tenemos la sección de configuración. Solo tenemos que definir el host, el usuario y la contraseña. En nuestro caso, localhost, originate y originatepass respectívamente.

Cuando invoquemos el script con nuestra url http://localhost/asterisk/originate.pl?orig=600&dest=1004 conectará con el manager para abrir el canal y hacer la llamada. Otra cosa que no os he dicho, este script se puede lanzar desde la propia línea de comandos pasándole como argumentos la extensión y el número:
perl originate.pl 600 1004

Con esto acabamos por hoy. Aunque no será el último artículo sobre Asterisk ;)

Thema: Asterisk | Kommentare (0) | Autor: Iñaki

Registrando robots de búsqueda en Apache

Wednesday, 3. October 2007 12:56

Hace unos días, un cliente me comentó su necesidad de loguear las entradas de los robots de búsqueda en un fichero aparte, para después procesarlas. Como me resultó novedoso el tema (en tanto que nunca he hecho algo parecido) me decidí a escribir un post. También han ayudado factores como recordarme que si me pagan el dominio, lo podría ir usando. Melón.

Vamos a ponernos en faena. Apache tiene una directiva para definir variables de entorno en función de una serie de condiciones. Me refiero a la directiva SetEnvIf. Para nuestro ejemplo también nos valdría BrowserMatch pero opté por la primera por ser más genérica. Simplificaré un poco el montaje (mi cliente tenía varios dominios virtuales). En nuestro caso lo vamos a hacer con mi dominio, por ejemplo.

Lo primero que tenemos que hacer es abrir el fichero donde definimos nuestro virtualhost. Sobre la línea que define el CustomLog escribimos:

SetEnvIf User-Agent bot is_a_robot
CustomLog /var/log/apache2/robots_access.log combined env=is_a_robot

Estas dos líneas sirven para definir la variable is_a_robot y activar el log en caso de que esa variable de entorno exista (con el tipo de log combined)

Fácil ¿no? Ya para terminar y rizar el rizo, hice un pequeño script en perl para separar cada bot en un fichero. Os lo pego aquí mismo (y os incluyo un enlace de paso aquí)

#!/usr/bin/perl

#
# Iñaki Rodriguez (2007)
#
# Split robots in separate files (using md5 as filename)
#
# License: GPL

use Digest::MD5 'md5_hex';

my $robot_log = '/var/log/apache2/robots_access.log';
my $output_dir = '/var/log/apache2/robots/';
my %bots;

open BOTLOG, "<$robot_log" or die ("$robot_log: I can't open it");
open INDEX, "<".$output_dir."index.txt";

while() {
    chomp();
    my ($md5,$ua) = split(/\t/);
    $bots{$md5} = $ua;
}
close(INDEX);

while() {

    chomp();
    m/^(.+?) (.+?) (.+?) (\[.+?\]) (".+?") (\d\d\d) (.+?) (".+?") (".*")$/;
    my $md5 = md5_hex($9);
    my $tmpf = $output_dir.$md5.".log";
    open LOG,">> $tmpf" or die ("$tmpf: I can't write it");
    print "Adding entry to $tmpf ($9)\n" if ($ARGV[0] ne '-q');
    $bots{$md5} = $9 if (!$bots{$md5});
    print LOG $_."\n";
    close(LOG);
}

close(BOTLOG);

open INDEX, ">".$output_dir."index.txt" or die($output_dir."index.txt: I can't create it");
foreach $md5 (keys %bots) {
    print INDEX $md5."\t".$bots{$md5}."\n";
}
close(INDEX);

Solo comentar un par de cosas del script. La variable $output_dir especifica el directorio donde va a guardar los logs por separado. En este mismo directorio se creará un fichero index.txt que guarda la correspondencia de cada log con su user agent. El nombre del fichero es la suma md5 del campo User Agent del log. Por último la variable $robot_log es la que establece el fichero donde se almacenan los registros de todos los robots.

Se podría mejorar. Invito al lector a añadir:

  • Registro de la última entrada para no duplicarlas
  • Exclusiones

Y hasta aquí el post del día.

Nota Mental: Arreglar el css que se descuadra (el theme no es mío)

Thema: Linux, Perl, Receta, apache | Kommentare (0) | Autor: Iñaki