#!/usr/bin/perl # Flash Operator Panel. http://www.asternic.org # # Copyright (c) 2004 Nicolás Gudiño. All rights reserved. # # Nicolás Gudiño # # This program is free software, distributed under the terms of # the GNU General Public License. # # THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO # EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; # OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF # ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use strict; use warnings; use integer; use constant DEBUG => 1; use constant BYTES_TO_READ => 256; use IO::Socket; use IO::Select; use Getopt::Long; use Pod::Usage; use Fcntl; use POSIX qw(setsid EWOULDBLOCK); my $FOP_VERSION = "0.30"; my %datos = (); my %chanvar = (); my %monitoring = (); my %passvar = (); my %sesbot = (); my %linkbot = (); my %cache_hit = (); my %estadoboton = (); my %preestadoboton = (); my %boton_paused = (); my %boton_agentready = (); my %boton_agentpaused = (); my %boton_agentbusy = (); my %boton_agentlogedof = (); my %botonled = (); my %botonalpha = (); my %botonledcolor = (); my %botonregistrado = (); my %boton_ip = (); my %botonlabel = (); my %botonlabelonly = (); my %botonsetlabel = (); my %botontimer = (); my %botontimertype = (); my %botonpark = (); my %botonmeetme = (); my %botonclid = (); my %botonpermanenttext = (); my %botonqueue = (); my %botonqueue_count = (); my %botonqueuemember = (); my %botonvoicemail = (); my %botonvoicemailcount = (); my %botonlinked = (); my %parked = (); my %meetme_pos = (); my %laststatus = (); my %autenticado = (); my %auto_conference = (); my %attendant_transfer = (); my %attendant_pending = (); my %pending_uniqueid_attendant = (); my %mute_other = (); my %autosip = (); my %cnt_auto_pos = (); my $cnt_autosip = 0; my %autosip_detail = (); my %buttons = (); my %buttons_queue = (); my %buttons_queue_reverse = (); my %buttons_preserve_case = (); my %buttons_astdbkey = (); my %button_server = (); my %buttons_reverse = (); my %textos = (); my %iconos = (); my %urls = (); my %alarms = (); my %targets = (); my %remote_callerid = (); my %remote_callerid_name = (); my %extension_transfer = (); my %extension_transfer_reverse = (); my %max_queue_waiting_time_for = (); my %flash_contexto = (); my %saved_clidnum = (); my %saved_clidname = (); my %keys_socket = (); my %manager_socket = (); my %start_muted = (); my %timeouts = (); my %no_rectangle = (); my %background = (); my %astdbcommands = (); my %client_queue = (); my %manager_queue = (); my %client_queue_nocrypt = (); my %ip_addy = (); my %held_channel = (); my %agents_available_on_queue = (); my $queue_object = {}; my %is_agent = (); my %agents_on_queue = (); my %max_lastcall = (); my $config = {}; my $cola = {}; my $language = {}; my $global_verbose = 1; my $help = 0; my $version = 0; my $counter_servers = -1; my %bloque_completo; my %buferbloque; my $bloque_final; my $todo; my $reload_pending = 0; my $regexp_buttons = 0; my $auto_buttons = 0; my @auto_config = (); my $queueagent_buttons = 0; my $defaultlanguage; my @bloque; my @respuestas; my @all_flash_files; my @masrespuestas; my @fake_bloque; my @flash_clients; my @status_active; my @panel_contexts; my %mailbox; my %tovoicemail; my %tospy; my %instancias; my %agent_to_channel; my %agent_label; my %togle_action; my %togle_response; my %channel_to_agent; my %reverse_agents; my %agents_name; my @p; my $m; my $O; my @S; my @key; my @manager_host = (); my @manager_port = (); my @manager_user = (); my @manager_secret = (); my @event_mask = (); my @astmanproxy_servers = (); my @manager_conectado = (); my %manager_desconectado; my %mask_hash; my $web_hostname; my $listen_port; my $park_exten; my $parktimeout; my $listen_addr; my $security_code; my $flash_dir; my $astmanproxy_server = ""; my $restrict_channel = ""; my $poll_interval; my $poll_voicemail; my $kill_zombies; my $ren_agentlogin; my $ren_cbacklogin; my $ren_agentname; my $agent_status; my $ren_queuemember; my $ren_wildcard; my $clid_privacy; my %clid_private; my %group_count; my $show_ip; my $queue_hide; my $enable_restart; my $passvars = ""; my $change_led; my $cdial_nosecure; my $barge_muted; my $debuglevel = -1; my $debuglevel_cache = ""; my $cont_debug_cache = 0; my $flash_file; my %barge_rooms; my %barge_context; my $first_room; my $last_room; my $clid_format; my $directorio = ""; my $auth_md5 = 1; my $astmanproxy_host = ""; my $astmanproxy_port = "5038"; my $md5challenge; my $reverse_transfer; my %shapes; my %legends; my %images; my %no_encryption = (); my %total_shapes; my %total_legends; my %total_images; my @serverinclude = (); my @btninclude = (); my @styleinclude = (); my $command = ""; my $daemonized = 0; my $pidfile = "/var/run/op_panel.pid"; my $logdir = ""; my $confdir = ""; my $tab = ""; my $PADDING = join( '', map( chr, ( 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ) ) ); my %a2b = ( A => 000, B => 001, C => 002, D => 003, E => 004, F => 005, G => 006, H => 007, I => 010, J => 011, K => 012, L => 013, M => 014, N => 015, O => 016, P => 017, Q => 020, R => 021, S => 022, T => 023, U => 024, V => 025, W => 026, X => 027, Y => 030, Z => 031, a => 032, b => 033, c => 034, d => 035, e => 036, f => 037, g => 040, h => 041, i => 042, j => 043, k => 044, l => 045, m => 046, n => 047, o => 050, p => 051, q => 052, r => 053, s => 054, t => 055, u => 056, v => 057, w => 060, x => 061, y => 062, z => 063, '0' => 064, '1' => 065, '2' => 066, '3' => 067, '4' => 070, '5' => 071, '6' => 072, '7' => 073, '8' => 074, '9' => 075, '+' => 076, '_' => 077, ); my %b2a = reverse %a2b; my $rand_byte_already_called = 0; $SIG{PIPE} = 'IGNORE'; $SIG{ALRM} = 'alarma_al_minuto'; $SIG{INT} = 'close_all'; $SIG{HUP} = 'generate_configs_onhup'; $SIG{USR1} = 'dump_internal_hashes_to_stdout'; GetOptions( 'p|pidfile=s' => \$pidfile, 'l|logdir=s' => \$logdir, 'c|confdir=s' => \$confdir, 'd|daemon' => \$daemonized, 'V|version' => \$version, 'x|debuglevel=i' => \$debuglevel, 'help|?' => \$help ); pod2usage(1) if $help; if ( $version == 1 ) { print "op_server.pl version $FOP_VERSION\n"; exit 0; } if ( $confdir eq "" ) { # if there is no config directory supplied at the command line # use the same directory where op_server.pl lives $directorio = $0; $directorio =~ s/(.*)\/(.*)/$1/g; } else { $directorio = $confdir; } if ( $logdir ne "" ) { open( STDOUT, ">>$logdir/output.log" ) or die "Can't open output log $logdir/error.log"; open( STDERR, ">>$logdir/error.log" ) or die "Can't open output log $logdir/error.log"; } if ( $daemonized == 1 ) { defined( my $pid = fork ) or die "Can't Fork: $!"; exit if $pid; setsid or die "Can't start a new session: $!"; open MYPIDFILE, ">$pidfile" or die "Failed to open PID file $pidfile for writing."; print MYPIDFILE $$; close MYPIDFILE; close(STDIN); if ( $logdir eq "" ) { close STDOUT; close STDERR; } } sub read_language_config() { $/ = "\n"; # tries to read and parse every language file needed foreach my $ctx (@panel_contexts) { if ( !defined( $config->{$ctx}{language} ) ) { $config->{"$ctx"}{language} = $defaultlanguage; } my $lang = $config->{$ctx}{language}; $lang =~ tr/A-Z/a-z/; $lang =~ s/\W//g; $config->{$ctx}{language} = $lang; open( CONFIG, "<$directorio/op_lang_$lang.cfg" ) or die("Could not open $directorio/op_lang_$lang.cfg. Aborting..."); while () { chop; $_ =~ s/^\s+//g; $_ =~ s/([^;]*)[;](.*)/$1/g; $_ =~ s/\s+$//g; next unless $_ ne ""; my ( $variable_name, $value ) = split( /=/, $_ ); $variable_name =~ tr/A-Z/a-z/; $variable_name =~ s/\s+//g; $value =~ s/^\s+//g; $value =~ s/\s+$//g; $value =~ s/\"//g; $language->{$ctx}{$variable_name} = $value; } close(CONFIG); } $/ = "\0"; } sub parse_amportal_config { my $filename = shift; my %ampconf; open( AMPCONF, $filename ) || die "Cannot open $filename"; while () { if ( $_ =~ /^\s*([a-zA-Z0-9]+)\s*=\s*(.*)\s*([;#].*)?/ ) { $ampconf{$1} = $2; } } close(AMPCONF); return %ampconf; } sub read_server_config() { my $context = ""; my @distinct_files; $counter_servers = -1; $/ = "\n"; @distinct_files = unique(@serverinclude); foreach my $archivo (@distinct_files) { open( CONFIG, "<$directorio/$archivo" ) or die("Could not open op_server.cfg. Aborting..."); while () { chomp; $_ =~ s/^\s+//g; $_ =~ s/([^;]*)[;](.*)/$1/g; $_ =~ s/\s+$//g; if ( /^#/ || /^;/ || /^$/ ) { next; } # Ignores comments and empty lines if (/^\Q[\E/) { s/\[(.*)\]/$1/g; tr/a-z/A-Z/; $context = $_; } else { if ( $context ne "" ) { my ( $variable_name, $value ) = split( /=/, $_ ); $variable_name =~ tr/A-Z/a-z/; $variable_name =~ s/\s+//g; $value =~ s/^\s+//g; $value =~ s/\s+$//g; $value =~ s/\"//g; $config->{$context}{$variable_name} = $value; if ( $variable_name eq "manager_host" ) { $counter_servers++; $manager_host[$counter_servers] = $value; } if ( $variable_name eq "manager_user" ) { $manager_user[$counter_servers] = $value; } if ( $variable_name eq "manager_secret" ) { $manager_secret[$counter_servers] = $value; } if ( $variable_name eq "manager_port" ) { $manager_port[$counter_servers] = $value; } if ( $variable_name eq "event_mask" ) { $event_mask[$counter_servers] = $value; } if ( $variable_name eq "astmanproxy_server" ) { push @astmanproxy_servers, $value; } } } } close(CONFIG); } if ( defined( $config->{GENERAL}{use_amportal_conf} ) ) { if ( $config->{GENERAL}{use_amportal_conf} == 1 ) { my $freepbx_config = "/etc/amportal.conf"; my %ampconf; if ( -e $freepbx_config ) { %ampconf = parse_amportal_config($freepbx_config); $config->{"GENERAL"}{"web_hostname"} = $ampconf{"AMPWEBADDRESS"}; $config->{"GENERAL"}{"security_code"} = $ampconf{"FOPPASSWORD"}; $config->{"GENERAL"}{"flash_dir"} = $ampconf{"FOPWEBROOT"}; $manager_user[0] = $ampconf{"AMPMGRUSER"}; $manager_secret[0] = $ampconf{"AMPMGRPASS"}; } } } $web_hostname = $config->{GENERAL}{web_hostname}; $listen_port = $config->{GENERAL}{listen_port}; $listen_addr = $config->{GENERAL}{listen_addr}; $security_code = $config->{GENERAL}{security_code}; $flash_dir = $config->{GENERAL}{flash_dir}; $poll_interval = $config->{GENERAL}{poll_interval}; $poll_voicemail = $config->{GENERAL}{poll_voicemail}; $kill_zombies = $config->{GENERAL}{kill_zombies}; $reverse_transfer = $config->{GENERAL}{reverse_transfer}; $auth_md5 = $config->{GENERAL}{auth_md5}; $astmanproxy_host = $config->{GENERAL}{astmanproxy_host}; $astmanproxy_port = $config->{GENERAL}{astmanproxy_port}; $ren_agentlogin = $config->{GENERAL}{rename_label_agentlogin}; $ren_cbacklogin = $config->{GENERAL}{rename_label_callbacklogin}; $ren_wildcard = $config->{GENERAL}{rename_label_wildcard}; $ren_agentname = $config->{GENERAL}{rename_to_agent_name}; $agent_status = $config->{GENERAL}{agent_status}; $ren_queuemember = $config->{GENERAL}{rename_queue_member}; $change_led = $config->{GENERAL}{change_led_agent}; $cdial_nosecure = $config->{GENERAL}{clicktodial_insecure}; $barge_muted = $config->{GENERAL}{barge_muted}; $clid_privacy = $config->{GENERAL}{clid_privacy}; $show_ip = $config->{GENERAL}{show_ip}; $queue_hide = $config->{GENERAL}{queue_hide}; $enable_restart = $config->{GENERAL}{enable_restart}; $defaultlanguage = $config->{GENERAL}{language}; $passvars = $config->{GENERAL}{passvars}; $park_exten = $config->{GENERAL}{parkexten}; $parktimeout = $config->{GENERAL}{parktimeout}; if ( $debuglevel == -1 ) { $debuglevel = $config->{GENERAL}{debug}; } my @todos_los_rooms; foreach my $val ($config) { while ( my ( $aa, $bb ) = each( %{$val} ) ) { while ( my ( $cc, $dd ) = each( %{$bb} ) ) { if ( $cc eq "barge_rooms" ) { ( $first_room, $last_room ) = split( /-/, $dd ); if ( !defined($last_room) ) { $last_room = $first_room; } my @arrayroom = $first_room .. $last_room; foreach (@arrayroom) { $barge_context{"$_"} = $aa; } push( @todos_los_rooms, @arrayroom ); } } } } %barge_rooms = map { $todos_los_rooms[$_], 0 } 0 .. $#todos_los_rooms; $clid_format = $config->{GENERAL}{clid_format}; if ( !defined($flash_dir) ) { $flash_dir = '/var/www/html' } $flash_file = $flash_dir . "/variables.txt"; push @all_flash_files, $flash_file; if ( !defined $web_hostname ) { $web_hostname = ""; } if ( !defined $listen_port ) { $listen_port = 4445; } if ( !defined $listen_addr ) { $listen_addr = "0.0.0.0"; } if ( !defined $astmanproxy_host ) { $astmanproxy_host = ""; } else { @manager_host = (); @manager_user = (); @manager_secret = (); push @manager_host, $astmanproxy_host; push @manager_user, "astmanproxy"; push @manager_secret, "astmanproxy"; } if ( defined $astmanproxy_port ) { @manager_port = (); push @manager_port, $astmanproxy_port; } if ( !defined $security_code ) { die("Missing security_code in op_server.cfg!"); } if ( !defined $flash_dir ) { die("Missing flash_dir in op_server.cfg!"); } if ( !defined $poll_interval ) { die("Missing poll_interval in op_server.cfg!"); } if ( !defined $ren_agentlogin ) { $ren_agentlogin = 0; } if ( !defined $defaultlanguage ) { $defaultlanguage = "en"; $config->{DEFAULT}{language} = "en"; } if ( !defined $config->{GENERAL}{monitor_filename} ) { $config->{GENERAL}{monitor_filename} = "\${UNIQUEID}"; } if ( !defined $config->{GENERAL}{monitor_format} ) { $config->{GENERAL}{monitor_format} = "wav"; } if ( !defined $clid_privacy ) { $clid_privacy = 0; } if ( !defined $show_ip ) { $show_ip = 0; } if ( !defined $queue_hide ) { $queue_hide = 0; } if ( !defined $ren_wildcard || $ren_wildcard eq "" ) { $ren_wildcard = 1; } if ( !defined $reverse_transfer || $reverse_transfer eq "" ) { $reverse_transfer = 0; } if ( !defined $barge_muted || $barge_muted eq "" ) { $barge_muted = 0; } if ( !defined $enable_restart || $enable_restart eq "" ) { $enable_restart = 0; } if ( !defined $cdial_nosecure || $cdial_nosecure eq "" ) { $cdial_nosecure = 0; } if ( !defined $agent_status || $agent_status eq "" ) { $agent_status = 0; } if ( !defined $ren_agentname || $ren_agentname eq "" ) { $ren_agentname = 0; } if ( !defined $ren_cbacklogin || $ren_cbacklogin eq "" ) { $ren_cbacklogin = 0; } if ( !defined $ren_queuemember || $ren_queuemember eq "" ) { $ren_queuemember = 0; } if ( !defined $change_led || $change_led eq "" ) { $change_led = 0; } if ( !defined $kill_zombies || $kill_zombies eq "" ) { $kill_zombies = 0; } if ( !defined $poll_voicemail || $poll_voicemail eq "" ) { $poll_voicemail = 0; } if ( !defined $clid_format ) { $clid_format = "(xxx) xxx-xxxx"; } if ( !defined $passvars ) { $passvars = ""; } if ( !defined $park_exten ) { $park_exten = "700"; } if ( !defined $parktimeout ) { $parktimeout = 45000; } else { $parktimeout = $parktimeout * 1000; } if ( !defined $debuglevel ) { $debuglevel = 0; } else { if ( $daemonized == 1 && $logdir eq "" ) { $debuglevel = 0; } } $/ = "\0"; } sub collect_includes { my $filename = shift; my $tipo = shift; my $archivo = $directorio . "/" . $filename; if ( !-r $archivo ) { log_debug( "** $archivo not readable... skipping", 16 ) if DEBUG; return; } if ( $tipo eq "buttons" ) { if ( !grep { $filename eq $_ } @btninclude ) { push( @btninclude, $filename ); } else { log_debug( "** $filename already included", 16 ) if DEBUG; return; } } if ( $tipo eq "style" ) { if ( !grep { $filename eq $_ } @styleinclude ) { push( @styleinclude, $filename ); } else { log_debug( "** $filename already included", 16 ) if DEBUG; return; } } if ( $tipo eq "server" ) { if ( !grep { $filename eq $_ } @serverinclude ) { push( @serverinclude, $filename ); } else { log_debug( "** $filename already included", 16 ) if DEBUG; return; } } open( CONFIG, "< $archivo" ) or die("Could not open $filename. Aborting...\n\n"); my @lineas = ; my $cuantos = @lineas; foreach my $linea (@lineas) { $linea =~ s/^\s+//g; $linea =~ s/([^;]*)[;](.*)/$1/g; $linea =~ s/\s+$//g; if ( $linea =~ /^include/ ) { # store include lines in an array so we can # process them later excluding duplicates $linea =~ s/^include//g; $linea =~ s/^\s+//g; $linea =~ s/^=>//g; $linea =~ s/^\s+//g; $linea =~ s/\s+$//g; collect_includes( $linea, $tipo ); } } close CONFIG; } sub read_astdb_config() { $/ = "\n"; if ( -e "$directorio/op_astdb.cfg" ) { open( ASTDB, "<$directorio/op_astdb.cfg" ) or die("Could not open op_astdb.cfg. Aborting..."); my $contador = 0; my $key = ""; while () { chomp; $_ =~ s/^\s+//g; $_ =~ s/([^;]*)[;](.*)/$1/g; $_ =~ s/\s+$//g; if ( /^#/ || /^;/ || /^$/ ) { next; } # Ignores comments and empty lines if (/^\Q[\E/) { s/\[(.*)\]/$1/g; $key = $_; } else { push @{ $astdbcommands{$key} }, $_; } } } close(ASTDB); $/ = "\0"; } sub read_buttons_config() { my @btn_cfg = (); my $contador = -1; my @distinct_files; my $no_counter = 0; my @contextos = (); my %lastposition = (); $/ = "\n"; @distinct_files = unique(@btninclude); foreach my $archivo (@distinct_files) { open( CONFIG, "< $directorio/$archivo" ) or die("Could not open $directorio/$archivo. Aborting..."); # Read op_buttons.cfg loading it into a hash for easier processing while () { chomp; $_ =~ s/^\s+//g; $_ =~ s/([^;]*)[;](.*)/$1/g; $_ =~ s/\s+$//g; if ( /^#/ || /^;/ || /^$/ ) { next; } # Ignores comments and empty lines if (/^\Q[\E/) { $contador++; s/\[(.*)\]/$1/g; my $channel = $_; $btn_cfg[$contador]{'channel_preserve_case'} = $channel; $btn_cfg[$contador]{'channel'} = $channel; } else { next unless ( $contador >= 0 ); my ( $key, $val ) = split( /=/, $_, 2 ); if ( !defined($val) ) { $val = ""; } $key =~ tr/A-Z/a-z/; $key =~ s/^\s+//g; $key =~ s/(.*)\s+/$1/g; if ( $key ne "label" && $key ne "font_family" && $key ne "text" && $key ne "mailbox" && $key ne "voicemail_context" ) { $val =~ s/^\s+//g; $val =~ s/(.*)\s+/$1/g; } $btn_cfg[$contador]{$key} = $val; if ( $key eq "panel_context" ) { push @contextos, $val; } } } close(CONFIG); } # Read now the auto_sip button config files foreach my $papi ( sort keys %autosip ) { if ( !defined( $autosip{$papi}{channel} ) ) { next; } $contador++; log_debug( "-----", 16 ) if DEBUG; $btn_cfg[$contador]{'channel_preserve_case'} = $autosip{$papi}{channel}; $btn_cfg[$contador]{'channel'} = $autosip{$papi}{channel}; if ( defined( $cnt_auto_pos{ $autosip{$papi}{autonumber} } ) ) { $cnt_auto_pos{ $autosip{$papi}{autonumber} }++; } else { $cnt_auto_pos{ $autosip{$papi}{autonumber} } = 0; } my $pos = $autosip{$papi}{starting_position} + $cnt_auto_pos{ $autosip{$papi}{autonumber} }; $btn_cfg[$contador]{'position'} = $pos; $btn_cfg[$contador]{'channel'} = $autosip{$papi}{channel}; my $logblock = "\n[" . $autosip{$papi}{channel} . "]\n"; log_debug( $autosip{$papi}{channel} . " at position $pos", 16 ) if DEBUG; while ( my ( $key, $val ) = each( %{ $autosip{$papi} } ) ) { if ( $val eq "" ) { log_debug( "** Empty value for autosip for key $key, button $papi", 1 ) if DEBUG; next; } $btn_cfg[$contador]{$key} = $val; $logblock .= "$key=$val\n"; if ( $key eq "panel_context" ) { push @contextos, $val; } } log_debug( "$logblock", 1 ) if DEBUG; } my @uniq2 = unique(@contextos); @contextos = @uniq2; @uniq2 = grep { !/\*/ } @contextos; @contextos = @uniq2; push @contextos, "DEFAULT"; push @contextos, "GENERAL"; # Convert every element to uppercase @panel_contexts = map { uc } @contextos; # Pass to replicate panel_context=* configuration my @copy_cfg = (); @copy_cfg = @btn_cfg; foreach (@copy_cfg) { my %tmphash = %$_; if ( defined( $tmphash{panel_context} ) ) { if ( $tmphash{panel_context} eq "*" ) { foreach my $contextoahora (@contextos) { $contador++; while ( my ( $key, $val ) = each(%tmphash) ) { if ( $key eq "panel_context" ) { $val = $contextoahora; } $btn_cfg[$contador]{$key} = $val; } } } } } # We finished reading the file, now we populate our # structures with the relevant data my %rectangles_counter; my %legends_counter; my %images_counter; my $cont_auto = 0; CONFIG: foreach (@btn_cfg) { my @positions = (); my %tmphash = %$_; if ( defined( $tmphash{panel_context} ) ) { if ( $tmphash{panel_context} eq "*" ) { # We skip the * panel_context because we already # expand them to every context possible before next CONFIG; } } if ( $tmphash{channel} =~ /^AUTO/i ) { $auto_buttons = 1; while ( my ( $key, $val ) = each(%tmphash) ) { $auto_config[$cont_auto]{$key} = $val; } $cont_auto++; next CONFIG; } if ( $tmphash{channel} =~ /^_/ ) { $regexp_buttons = 1; } elsif ( $tmphash{channel} =~ /^QUEUEAGENT\//i ) { $queueagent_buttons = 1; } elsif ( $tmphash{channel} =~ /^image$/i ) { # Image config primitive if ( defined( $tmphash{panel_context} ) ) { $tmphash{panel_context} =~ tr/a-z/A-Z/; $tmphash{panel_context} =~ s/^DEFAULT$//xms; } else { $tmphash{panel_context} = ""; } my $conttemp = $tmphash{panel_context}; if ( $conttemp eq "" ) { $conttemp = "GENERAL"; } if ( !defined( $tmphash{src} ) ) { next CONFIG; } if ( !defined( $tmphash{url} ) ) { $tmphash{url} = "no"; } if ( !defined( $tmphash{target} ) ) { $tmphash{target} = "NONTARFOP"; } $images_counter{$conttemp}++; if ( $images_counter{$conttemp} > 1 ) { $images{$conttemp} .= "&"; } $total_images{$conttemp}++; $images{$conttemp} .= "image_$images_counter{$conttemp}=" . $tmphash{x} . ","; $images{$conttemp} .= $tmphash{y} . ","; $images{$conttemp} .= $tmphash{src} . ","; $images{$conttemp} .= $tmphash{url} . ","; $images{$conttemp} .= $tmphash{target}; next CONFIG; } elsif ( $tmphash{channel} =~ /^legend$/i ) { # Legend config primitive if ( defined( $tmphash{panel_context} ) ) { $tmphash{panel_context} =~ tr/a-z/A-Z/; $tmphash{panel_context} =~ s/^DEFAULT$//xms; } else { $tmphash{panel_context} = ""; } my $conttemp = $tmphash{panel_context}; if ( $conttemp eq "" ) { $conttemp = "GENERAL"; } if ( !defined( $tmphash{text} ) ) { $tmphash{text} = "LEGEND"; } if ( !defined( $tmphash{x} ) ) { $tmphash{x} = 1; } if ( !defined( $tmphash{y} ) ) { $tmphash{y} = 1; } if ( !defined( $tmphash{font_size} ) ) { $tmphash{font_size} = 16; } if ( !defined( $tmphash{font_color} ) ) { $tmphash{font_color} = "000000"; } if ( !defined( $tmphash{use_embed_fonts} ) ) { $tmphash{use_embed_fonts} = 1; } if ( !defined( $tmphash{font_family} ) ) { $tmphash{font_family} = "Arial"; } if ( !defined( $tmphash{no_base64} ) ) { $tmphash{no_base64} = 0; } if ( $tmphash{no_base64} == 0 ) { $tmphash{text} = encode_base64( $tmphash{text} ); } if ( !defined( $tmphash{url} ) ) { $tmphash{url} = "no"; } if ( !defined( $tmphash{target} ) ) { $tmphash{target} = "NONTARFOP"; } $legends_counter{$conttemp}++; if ( $legends_counter{$conttemp} > 1 ) { $legends{$conttemp} .= "&"; } $total_legends{$conttemp}++; $legends{$conttemp} .= "legend_$legends_counter{$conttemp}=" . $tmphash{x} . ","; $legends{$conttemp} .= $tmphash{y} . ","; $legends{$conttemp} .= $tmphash{text} . ","; $legends{$conttemp} .= $tmphash{font_size} . ","; $legends{$conttemp} .= $tmphash{font_family} . ","; $legends{$conttemp} .= $tmphash{font_color} . ","; $legends{$conttemp} .= $tmphash{use_embed_fonts} . ","; $legends{$conttemp} .= $tmphash{no_base64} . ","; $legends{$conttemp} .= $tmphash{url} . ","; $legends{$conttemp} .= $tmphash{target}; next CONFIG; } elsif ( $tmphash{channel} =~ /^RECTANGLE$/i ) { # Rectangle config primitive if ( defined( $tmphash{panel_context} ) ) { $tmphash{panel_context} =~ tr/a-z/A-Z/; $tmphash{panel_context} =~ s/^DEFAULT$//; } else { $tmphash{panel_context} = ""; } my $conttemp = $tmphash{panel_context}; if ( $conttemp eq "" ) { $conttemp = "GENERAL"; } if ( !defined( $tmphash{x} ) ) { $tmphash{x} = 1; } if ( !defined( $tmphash{y} ) ) { $tmphash{y} = 1; } if ( !defined( $tmphash{width} ) ) { $tmphash{width} = 1; } if ( !defined( $tmphash{height} ) ) { $tmphash{height} = 1; } if ( !defined( $tmphash{line_width} ) ) { $tmphash{line_width} = 1; } if ( !defined( $tmphash{line_color} ) ) { $tmphash{line_color} = "0x000000"; } if ( !defined( $tmphash{fade_color1} ) ) { $tmphash{fade_color1} = "0xd0d0d0"; } if ( !defined( $tmphash{fade_color2} ) ) { $tmphash{fade_color2} = "0xd0d000"; } if ( !defined( $tmphash{rnd_border} ) ) { $tmphash{rnd_border} = 3; } if ( !defined( $tmphash{alpha} ) ) { $tmphash{alpha} = 100; } if ( !defined( $tmphash{layer} ) ) { $tmphash{layer} = "bottom"; } $rectangles_counter{$conttemp}++; if ( $rectangles_counter{$conttemp} > 1 ) { $shapes{$conttemp} .= "&"; } $total_shapes{$conttemp}++; $shapes{$conttemp} .= "rect_$rectangles_counter{$conttemp}=" . $tmphash{x} . ","; $shapes{$conttemp} .= $tmphash{y} . ","; $shapes{$conttemp} .= $tmphash{width} . ","; $shapes{$conttemp} .= $tmphash{height} . ","; $shapes{$conttemp} .= $tmphash{line_width} . ","; $shapes{$conttemp} .= $tmphash{line_color} . ","; $shapes{$conttemp} .= $tmphash{fade_color1} . ","; $shapes{$conttemp} .= $tmphash{fade_color2} . ","; $shapes{$conttemp} .= $tmphash{rnd_border} . ","; $shapes{$conttemp} .= $tmphash{alpha} . ","; $shapes{$conttemp} .= $tmphash{layer}; next CONFIG; } if ( !defined( $tmphash{position} ) ) { log_debug( "** Ignored button $tmphash{'channel'}, position?", 16 ) if DEBUG; next CONFIG; } if ( !defined( $tmphash{alarm} ) ) { $tmphash{alarm} = "0"; } if ( !defined( $tmphash{url} ) ) { $tmphash{url} = "0"; } if ( !defined( $tmphash{target} ) ) { $tmphash{target} = "0"; } if ( !defined( $tmphash{server} ) ) { $tmphash{server} = 0; } else { if ( $tmphash{server} eq "*" ) { $tmphash{server} = 0; } $tmphash{server} = $tmphash{server} - 1; } if ( !defined( $tmphash{label} ) ) { $tmphash{label} = $tmphash{channel}; } if ( !defined( $tmphash{icon} ) ) { $tmphash{icon} = "0"; } # Local channels are case sensitive my $canal_key = ""; if ( $tmphash{channel} =~ m/^local/i ) { $canal_key = $tmphash{channel}; } else { $canal_key = uc( $tmphash{channel} ); } my $canal_key_case = $tmphash{channel_preserve_case}; if ( $canal_key =~ m/^PARK\d/ ) { # Change the PARKXXX tu PARK/XXX $canal_key =~ s/PARK(.*)/PARK\/$1/g; $canal_key_case =~ s/PARK(.*)/PARK\/$1/gi; } if ( defined( $tmphash{panel_context} ) ) { $tmphash{panel_context} =~ tr/a-z/A-Z/; $tmphash{panel_context} =~ s/^DEFAULT$//; } else { $tmphash{panel_context} = ""; } if ( $tmphash{panel_context} ne "" ) { # We want to add the context in case we have the same button # repeated in several panel_contexts. If we do not add it, then # only the last panel context will prevail. $canal_key .= "&" . $tmphash{panel_context}; $canal_key_case .= "&" . $tmphash{panel_context}; } if ( ( $tmphash{position} !~ /,/ ) && ( $tmphash{position} !~ /-/ ) && ( $canal_key =~ /^_/ ) ) { # If it's a regexp button with just one position # we fake the same position number to populate # the array and make the button work anyways. my $pos = $tmphash{position}; $pos =~ s/(\d+),(\d+)/$1/g; my $countpos = 2; $tmphash{position} = ""; if ( defined( $tmphash{count} ) ) { $countpos = $tmphash{count}; } my $a = 0; for ( $a = 0 ; $a < $countpos ; $a++ ) { $tmphash{position} .= "$pos,"; } $tmphash{position} = substr( $tmphash{position}, 0, -1 ); $no_counter = 1; } else { $no_counter = 0; } if ( $tmphash{position} =~ /[,-]/ ) { my $canalidx = $tmphash{server} . "^" . $tmphash{channel}; if ( defined( $tmphash{panel_context} ) && $tmphash{panel_context} ne "" ) { $canalidx .= "&" . $tmphash{panel_context}; } $instancias{ uc($canalidx) }{""} = 0; my @ranges = split( /,/, $tmphash{position} ); foreach my $valu (@ranges) { if ( $valu !~ m/-/ ) { if ( $valu eq "n" ) { my $lastpos = $lastposition{ $tmphash{panel_context} }; if ( is_number($lastpos) ) { $lastpos++; $lastposition{ $tmphash{panel_context} } = $lastpos; $valu = $lastpos; push @positions, $valu; last; } } push @positions, $valu; } else { my @range2 = split( /-/, $valu ); my $menor = $range2[0] < $range2[1] ? $range2[0] : $range2[1]; my $mayor = $range2[0] > $range2[1] ? $range2[0] : $range2[1]; my @newrange = $menor .. $mayor; foreach my $valevale (@newrange) { push @positions, $valevale; } } } my $count = 0; foreach my $pos (@positions) { $count++; my $indice_contexto = $pos; my $chan_trunk = $tmphash{channel} . "=" . $count; my $chan_trunk_case = $tmphash{channel_preserve_case} . "=" . $count; if ( $tmphash{panel_context} ne "" ) { $chan_trunk .= "&" . $tmphash{panel_context}; $chan_trunk_case .= "&" . $tmphash{panel_context}; $indice_contexto .= "@" . $tmphash{panel_context}; $pos .= "@" . $tmphash{panel_context}; } if ( $chan_trunk =~ m/^QUEUE/i ) { $buttons_queue{ uc("$tmphash{server}^$chan_trunk") } = $pos; } $buttons_preserve_case{"$tmphash{server}^$chan_trunk_case"} = $pos; if ( defined( $tmphash{astdbkey} ) ) { $buttons_astdbkey{"$tmphash{server}^$chan_trunk_case"} = $tmphash{astdbkey}; } $buttons{ uc("$tmphash{server}^$chan_trunk") } = $pos; $textos{$indice_contexto} = $tmphash{label}; if ( !defined( $tmphash{no_label_counter} ) ) { $tmphash{no_label_counter} = 0; } if ( $no_counter == 0 && $tmphash{no_label_counter} == 0 ) { $textos{$indice_contexto} .= " " . $count; } $iconos{$indice_contexto} = $tmphash{icon}; $urls{$indice_contexto} = $tmphash{url}; $alarms{$indice_contexto} = $tmphash{alarm}; $targets{$indice_contexto} = $tmphash{target}; $button_server{$pos} = $tmphash{server}; # Saves last position for the button@context $lastposition{ $tmphash{panel_context} } = $pos; log_debug( qq[** $tmphash{server}^$chan_trunk in position $pos], 16 ) if DEBUG; } } else { my $lastpos = 0; $lastpos = $lastposition{ $tmphash{panel_context} } if defined( $lastposition{ $tmphash{panel_context} } ); if ( $tmphash{position} eq "n" ) { if ( is_number($lastpos) ) { $lastpos++; $lastposition{ $tmphash{panel_context} } = $lastpos; } } else { $lastpos = $tmphash{position}; $lastposition{ $tmphash{panel_context} } = $lastpos; } log_debug( qq[** $tmphash{channel} in next position $lastpos], 16 ) if DEBUG; if ( $tmphash{panel_context} ne "" ) { if ( $canal_key =~ m/^QUEUE/i ) { $buttons_queue{ uc("$tmphash{server}^$canal_key") } = $lastpos . "\@" . $tmphash{panel_context}; } $buttons{"$tmphash{server}^$canal_key"} = $lastpos . "\@" . $tmphash{panel_context}; $buttons_preserve_case{"$tmphash{server}^$canal_key_case"} = $lastpos . "\@" . $tmphash{panel_context}; if ( defined( $tmphash{astdbkey} ) ) { $buttons_astdbkey{"$tmphash{server}^$canal_key_case"} = $tmphash{astdbkey}; } $textos{"$lastpos\@$tmphash{panel_context}"} = $tmphash{label}; $iconos{"$lastpos\@$tmphash{panel_context}"} = $tmphash{icon}; $urls{"$lastpos\@$tmphash{panel_context}"} = $tmphash{url}; $alarms{"$lastpos\@$tmphash{panel_context}"} = $tmphash{alarm}; $targets{"$lastpos\@$tmphash{panel_context}"} = $tmphash{target}; $button_server{ $buttons{"$tmphash{server}^$canal_key"} } = $tmphash{server}; } else { if ( $canal_key =~ /^_/ ) { $canal_key .= "=1"; } if ( $canal_key =~ m/^QUEUE/i ) { $buttons_queue{ uc("$tmphash{server}^$canal_key") } = $lastpos; } $buttons{"$tmphash{server}^$canal_key"} = $lastpos; $buttons_preserve_case{"$tmphash{server}^$canal_key_case"} = $lastpos; if ( defined( $tmphash{astdbkey} ) ) { $buttons_astdbkey{"$tmphash{server}^$canal_key_case"} = $tmphash{astdbkey}; } $textos{$lastpos} = $tmphash{label}; $iconos{$lastpos} = $tmphash{icon}; $urls{$lastpos} = $tmphash{url}; $alarms{$lastpos} = $tmphash{alarm}; $targets{$lastpos} = $tmphash{target}; $button_server{ $buttons{"$tmphash{server}^$canal_key"} } = $tmphash{server}; } } @positions = unique(@positions); if ( defined( $tmphash{groupcount} ) ) { my $count = @positions; if ( $count == 0 ) { push @positions, $lastposition{ $tmphash{panel_context} }; } if ( $tmphash{groupcount} eq "true" || $tmphash{groupcount} eq "1" ) { my $agre_context = ""; if ( $tmphash{panel_context} ne "" ) { $agre_context = "\@" . $tmphash{panel_context}; } foreach my $pos (@positions) { $group_count{"$pos$agre_context"} = 1; } } } if ( defined( $tmphash{privacy} ) ) { my $count = @positions; if ( $count == 0 ) { push @positions, $lastposition{ $tmphash{panel_context} }; } if ( $tmphash{privacy} eq "true" || $tmphash{privacy} eq "1" ) { my $agre_context = ""; if ( $tmphash{panel_context} ne "" ) { $agre_context = "\@" . $tmphash{panel_context}; } foreach my $pos (@positions) { $clid_private{"$pos$agre_context"} = 1; } } } if ( defined( $tmphash{no_rectangle} ) ) { my $count = @positions; if ( $count == 0 ) { push @positions, $lastposition{ $tmphash{panel_context} }; } if ( $tmphash{no_rectangle} eq "true" || $tmphash{no_rectangle} eq "1" ) { my $pcont = $tmphash{panel_context}; if ( $pcont eq "" ) { $pcont = "GENERAL"; } foreach my $pos (@positions) { $pos =~ s/\@$pcont//g; $no_rectangle{$pcont}{$pos} = 1; } } } if ( defined( $tmphash{background} ) ) { my $count = @positions; if ( $count == 0 ) { push @positions, $lastposition{ $tmphash{panel_context} }; } my $pcont = $tmphash{panel_context}; if ( $pcont eq "" ) { $pcont = "GENERAL"; } foreach my $pos (@positions) { $pos =~ s/\@$pcont//g; $background{$pcont} .= "&bg$pos=$tmphash{background}"; } } if ( defined( $tmphash{extension} ) ) { if ( defined( $tmphash{context} ) ) { $extension_transfer{"$tmphash{server}^$canal_key"} = $tmphash{server} . "^" . $tmphash{extension} . "@" . $tmphash{context}; } else { $extension_transfer{"$tmphash{server}^$canal_key"} = $tmphash{server} . "^" . $tmphash{extension}; } if ( defined( $tmphash{voicemail_context} ) ) { $mailbox{"$tmphash{server}^$canal_key"} = $tmphash{extension} . "@" . $tmphash{voicemail_context}; } } if ( defined( $tmphash{mailbox} ) ) { $mailbox{"$tmphash{server}^$canal_key"} = $tmphash{mailbox}; } if ( defined( $tmphash{voicemailext} ) ) { my $indicevm = $lastposition{ $tmphash{panel_context} }; if ( $tmphash{panel_context} ne "" ) { $indicevm .= "\@$tmphash{panel_context}"; } $tovoicemail{$indicevm} = $tmphash{voicemailext}; } if ( defined( $tmphash{spyext} ) ) { my $indicespy = $lastposition{ $tmphash{panel_context} }; if ( $tmphash{panel_context} ne "" ) { $indicespy .= "\@$tmphash{panel_context}"; } $tospy{$indicespy} = $tmphash{spyext}; } $/ = "\0"; } %extension_transfer_reverse = reverse %extension_transfer; %buttons_reverse = reverse %buttons; %buttons_queue_reverse = reverse %buttons_queue; } sub genera_config { # This sub generates the file variables.txt that is read by the # swf movie on load, with info about buttons, layout, etc. my @textsclients = ( 'detail_title', 'detail_from', 'detail_to', 'security_code_title', 'btn_security_text', 'btn_restart_text', 'btn_reload_text', 'btn_debug_text', 'btn_help_text', 'tab_call_text', 'tab_queue_text', 'calls_taken_text', 'no_data_text', 'debug_window_title', 'detail_duration', 'clid_label', 'version_mismatch' ); $/ = "\n"; my %style_variables; my @contextos = (); my @unique_contexts = (); my $contextoactual = ""; my $highest_position = 0; my @style_include = (); foreach my $archi (@styleinclude) { open( STYLE, "<$directorio/$archi" ) or die("Could not open $archi for reading"); while (