summaryrefslogtreecommitdiff
path: root/kernel/xpp/utils/zconf/Zaptel/Config
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/xpp/utils/zconf/Zaptel/Config')
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Defaults.pm12
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Gen.pm212
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm70
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm177
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm213
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm158
-rw-r--r--kernel/xpp/utils/zconf/Zaptel/Config/Params.pm149
7 files changed, 991 insertions, 0 deletions
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Defaults.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Defaults.pm
index 360ca0a..9a3dec9 100644
--- a/kernel/xpp/utils/zconf/Zaptel/Config/Defaults.pm
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Defaults.pm
@@ -1,4 +1,16 @@
package Zaptel::Config::Defaults;
+
+#
+# !!!! DEPRECATION WARNING!!!
+#
+# This module is used by older versions of init_card_* scripts and
+# their related drivers.
+#
+# Zaptel drivers *after* 1.4.12.1 and DAHDI drivers do not use it.
+# Their configuration is read via XppConfig.pm which is always
+# bundled with the init_card_* scripts.
+#
+
#
# Written by Oron Peled <oron@actcom.co.il>
# Copyright (C) 2007, Xorcom
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen.pm
new file mode 100644
index 0000000..05bb9be
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen.pm
@@ -0,0 +1,212 @@
+package Zaptel::Config::Gen;
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2009, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+
+=head1 NAME
+
+Zaptel::Config::Gen -- Wrapper class for configuration generators.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Gen qw(is_true);
+ my $params = Zaptel::Config::Params->new('the-config-file');
+ my $gconfig = Zaptel::Config::Gen->new($params);
+ my $num = $gconfig->{'base_exten'};
+ my $overlap = is_true($gconfig->{'brint_overlap'});
+ $gconfig->dump; # For debugging
+ $gconfig->run_generator('system', {}, @spans);
+
+=head1 DESCRIPTION
+
+The constructor must be given an C<Zaptel::Config::Params> object.
+The returned object contains all data required for generation in the
+form of a hash.
+
+The constructor maps the C<item()>s from the parameter object into semantic
+configuration keys. E.g: the C<lc_country> item is mapped to C<loadzone> and
+C<defaultzone> keys.
+
+The actual generation is done by delegation to one of the generators.
+This is done via the C<run_generator()> method which receive the
+generator name, a generator specific options hash and a list of
+span objects (from C<Zaptel::Span>) for which to generate configuration.
+
+This module contains few helper functions. E.g: C<is_true()>, C<bchan_range()>.
+
+=cut
+
+require Exporter;
+@ISA = qw(Exporter);
+
+@EXPORT_OK = qw(is_true);
+
+use strict;
+
+# Parse values as true/false
+sub is_true($) {
+ my $val = shift;
+ return undef unless defined $val;
+ return $val =~ /^(1|y|yes)$/i;
+}
+
+# Generate channel range strings from span objects
+# E.g: "63-77,79-93"
+sub bchan_range($) {
+ my $span = shift || die;
+ my $first_chan = ($span->chans())[0];
+ my $first_num = $first_chan->num();
+ my $range_start = $first_num;
+ my @range;
+ my $prev = undef;
+
+ die unless $span->is_digital();
+ foreach my $c (@{$span->bchan_list()}) {
+ my $curr = $c + $first_num;
+ if(!defined($prev)) {
+ $prev = $curr;
+ } elsif($curr != $prev + 1) {
+ push(@range, sprintf("%d-%d", $range_start, $prev));
+ $range_start = $curr;
+ }
+ $prev = $curr;
+ }
+ if($prev >= $first_num) {
+ push(@range, sprintf("%d-%d", $range_start, $prev));
+ }
+ return join(',', @range);
+}
+
+sub new($) {
+ my $pack = shift || die "$0: Missing package argument";
+ my $p = shift || die "$0: Missing parameters argument";
+
+ # Set defaults
+ my $fxs_default_start = $p->item('fxs_default_start');
+ my $fxo_default_start = $p->item('fxo_default_start');
+
+ my %default_context = (
+ FXO => $p->item('context_lines'),
+ FXS => $p->item('context_phones'),
+ IN => $p->item('context_input'),
+ OUT => $p->item('context_output'),
+ BRI_TE => $p->item('context_lines'),
+ BRI_NT => $p->item('context_phones'),
+ E1_TE => $p->item('context_lines'),
+ T1_TE => $p->item('context_lines'),
+ J1_TE => $p->item('context_lines'),
+ E1_NT => $p->item('context_phones'),
+ T1_NT => $p->item('context_phones'),
+ J1_NT => $p->item('context_phones'),
+ );
+ my %default_group = (
+ FXO => $p->item('group_lines'),
+ FXS => $p->item('group_phones'),
+ IN => '',
+ OUT => '',
+ BRI_TE => $p->item('group_lines'),
+ BRI_NT => $p->item('group_lines'),
+ E1_TE => $p->item('group_lines'),
+ T1_TE => $p->item('group_lines'),
+ J1_TE => $p->item('group_lines'),
+ E1_NT => $p->item('group_lines'),
+ T1_NT => $p->item('group_lines'),
+ J1_NT => $p->item('group_lines'),
+ );
+ my %default_zaptel_signalling = (
+ FXO => "fxs$fxo_default_start",
+ FXS => "fxo$fxs_default_start",
+ IN => "fxo$fxs_default_start",
+ OUT => "fxo$fxs_default_start",
+ );
+ my %default_zapata_signalling = (
+ FXO => "fxs_$fxo_default_start",
+ FXS => "fxo_$fxs_default_start",
+ IN => "fxo_$fxs_default_start",
+ OUT => "fxo_$fxs_default_start",
+ );
+
+ # First complex mapping
+ my $gconfig = {
+ PARAMETERS => $p,
+ 'loadzone' => $p->item('lc_country'),
+ 'defaultzone' => $p->item('lc_country'),
+ 'context' => \%default_context,
+ 'group' => \%default_group,
+ 'zaptel_signalling' => \%default_zaptel_signalling,
+ 'zapata_signalling' => \%default_zapata_signalling,
+ };
+ # Now add trivial mappings
+ my @trivial = qw(
+ base_exten
+ freepbx
+ fxs_immediate
+ bri_hardhdlc
+ bri_sig_style
+ r2_idle_bits
+ brint_overlap
+ pri_termtype
+ pri_connection_type
+ );
+ foreach my $k (@trivial) {
+ $gconfig->{$k} = $p->item($k);
+ }
+ bless $gconfig,$pack;
+
+ return $gconfig;
+}
+
+sub run_generator($$@) {
+ my $gconfig = shift || die;
+ my $name = shift || die "$0: Missing generator name argument";
+ my $genopts = shift || die "$0: Missing genopts argument";
+ ref($genopts) eq 'HASH' or die "$0: Bad genopts argument";
+ my @spans = @_;
+
+ my $module = "Zaptel::Config::Gen::$name";
+ #print STDERR "DEBUG: $module\n";
+ eval "use $module";
+ if($@) {
+ die "Failed to load configuration generator for '$name'\n";
+ }
+ my $cfg = $module->new($gconfig, $genopts);
+ $cfg->generate(@spans);
+}
+
+sub dump($) {
+ my $self = shift || die;
+ printf STDERR "%s dump:\n", ref $self;
+ my $width = 30;
+ foreach my $k (sort keys %$self) {
+ my $val = $self->{$k};
+ my $ref = ref $val;
+ #print STDERR "DEBUG: '$k', '$ref', '$val'\n";
+ if($ref eq '') {
+ printf STDERR "%-${width}s %s\n", $k, $val;
+ } elsif($ref eq 'SCALAR') {
+ printf STDERR "%-${width}s %s\n", $k, ${$val};
+ } elsif($ref eq 'ARRAY') {
+ #printf STDERR "%s:\n", $k;
+ my $i = 0;
+ foreach my $v (@{$val}) {
+ printf STDERR "%-${width}s %s\n", "$k\->[$i]", $v;
+ $i++;
+ }
+ } elsif($ref eq 'HASH') {
+ #printf STDERR "%s:\n", $k;
+ foreach my $k1 (keys %{$val}) {
+ printf STDERR "%-${width}s %s\n", "$k\->\{$k1\}", ${$val}{$k1};
+ }
+ } else {
+ printf STDERR "%-${width}s (-> %s)\n", $k, $ref;
+ }
+ }
+}
+
+
+1;
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm
new file mode 100644
index 0000000..3e1048d
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Unicall.pm
@@ -0,0 +1,70 @@
+package Zaptel::Config::Gen::Unicall;
+use strict;
+
+use Zaptel::Config::Gen qw(is_true);
+
+sub new($$$) {
+ my $pack = shift || die;
+ my $gconfig = shift || die;
+ my $genopts = shift || die;
+ my $file = $ENV{UNICALL_CHANNELS_FILE} || "/etc/asterisk/unicall-channels.conf";
+ my $self = {
+ FILE => $file,
+ GCONFIG => $gconfig,
+ GENOPTS => $genopts,
+ };
+ bless $self, $pack;
+ return $self;
+}
+
+sub generate($) {
+ my $self = shift || die;
+ my $file = $self->{FILE};
+ my $gconfig = $self->{GCONFIG};
+ my $genopts = $self->{GENOPTS};
+ #$gconfig->dump;
+ my @spans = @_;
+ warn "Empty configuration -- no spans\n" unless @spans;
+ die "Only for R2" unless $gconfig->{'pri_connection_type'} eq 'R2';
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ print "Generating $file\n" if $genopts->{verbose};
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ printf "; Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime);
+ print "; This file should be #included in unicall.conf\n\n";
+ foreach my $span (@spans) {
+ next unless $span->is_digital();
+ printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ my $idle_bits = $gconfig->{'r2_idle_bits'};
+ printf "protocolend=%s\n", ($span->termtype() eq 'TE') ? 'cpe' : 'co';
+ printf "channel=%s\n", Zaptel::Config::Gen::bchan_range($span);
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+unicall - Generate configuration for unicall channels.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Gen::Unicall;
+
+ my $cfg = new Zaptel::Config::Gen::Unicall(\%global_config, \%genopts);
+ $cfg->generate(@span_list);
+
+=head1 DESCRIPTION
+
+Generate the F</etc/asterisk/unicall-channels.conf> to be included in
+F</etc/asterisk/unicall.conf>
+
+Its location may be overriden via the environment variable
+C<UNICALL_CHANNELS_FILE>.
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm
new file mode 100644
index 0000000..7c9e937
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Users.pm
@@ -0,0 +1,177 @@
+package Zaptel::Config::Gen::Users;
+use strict;
+
+use File::Basename;
+use Zaptel::Config::Gen qw(is_true);
+
+sub new($$$) {
+ my $pack = shift || die;
+ my $gconfig = shift || die;
+ my $genopts = shift || die;
+ my $file = $ENV{USERS_FILE} || "/etc/asterisk/users.conf";
+ my $self = {
+ FILE => $file,
+ GCONFIG => $gconfig,
+ GENOPTS => $genopts,
+ };
+ bless $self, $pack;
+ return $self;
+}
+
+sub gen_channel($) {
+ my $self = shift || die;
+ my $chan = shift || die;
+ my $gconfig = $self->{GCONFIG};
+ my $type = $chan->type;
+ my $num = $chan->num;
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ my $exten = $gconfig->{'base_exten'} + $num;
+ my $sig = $gconfig->{'zapata_signalling'}{$type};
+ my $full_name = "$type $num";
+
+ die "missing default_zapata_signalling for chan #$num type $type" unless $sig;
+ print << "EOF";
+[$exten]
+callwaiting = yes
+context = numberplan-custom-1
+fullname = $full_name
+cid_number = $exten
+hasagent = no
+hasdirectory = no
+hasiax = no
+hasmanager = no
+hassip = no
+hasvoicemail = yes
+host = dynamic
+mailbox = $exten
+threewaycalling = yes
+vmsecret = 1234
+secret = 1234
+signalling = $sig
+zapchan = $num
+registeriax = no
+registersip = no
+canreinvite = no
+nat = no
+dtmfmode = rfc2833
+disallow = all
+allow = all
+
+EOF
+}
+
+# generate users.conf . The specific users.conf is strictly oriented
+# towards using with the asterisk-gui .
+#
+# This code could have generated a much simpler and smaller
+# configuration file, had there been minimal level of support for
+# configuration templates in the asterisk configuration rewriting. Right
+# now Asterisk's configuration rewriting simply freaks out in the face
+# of templates: http://bugs.digium.com/11442 .
+sub generate($) {
+ my $self = shift || die;
+ my $file = $self->{FILE};
+ my $gconfig = $self->{GCONFIG};
+ my $genopts = $self->{GENOPTS};
+ #$gconfig->dump;
+ my @spans = @_;
+ warn "Empty configuration -- no spans\n" unless @spans;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ print "Generating $file\n" if $genopts->{verbose};
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ print <<"HEAD";
+;!
+;! Automatically generated configuration file
+;! Filename: @{[basename($file)]} ($file)
+;! Generator: $0
+;! Creation Date: @{[scalar(localtime)]}
+;!
+[general]
+;
+; Full name of a user
+;
+fullname = New User
+;
+; Starting point of allocation of extensions
+;
+userbase = @{[$gconfig->{'base_exten'}+1]}
+;
+; Create voicemail mailbox and use use macro-stdexten
+;
+hasvoicemail = yes
+;
+; Set voicemail mailbox @{[$gconfig->{'base_exten'}+1]} password to 1234
+;
+vmsecret = 1234
+;
+; Create SIP Peer
+;
+hassip = no
+;
+; Create IAX friend
+;
+hasiax = no
+;
+; Create Agent friend
+;
+hasagent = no
+;
+; Create H.323 friend
+;
+;hash323 = yes
+;
+; Create manager entry
+;
+hasmanager = no
+;
+; Remaining options are not specific to users.conf entries but are general.
+;
+callwaiting = yes
+threewaycalling = yes
+callwaitingcallerid = yes
+transfer = yes
+canpark = yes
+cancallforward = yes
+callreturn = yes
+callgroup = 1
+pickupgroup = 1
+localextenlength = @{[length($gconfig->{'base_exten'})]}
+
+
+HEAD
+ foreach my $span (@spans) {
+ next unless grep { $_ eq $span->type} ( 'FXS', 'IN', 'OUT' );
+ printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ foreach my $chan ($span->chans()) {
+ $self->gen_channel($chan);
+ }
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+users - Generate configuration for users.conf.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Gen::Users;
+
+ my $cfg = new Zaptel::Config::Gen::Users(\%global_config, \%genopts);
+ $cfg->generate(@span_list);
+
+=head1 DESCRIPTION
+
+Generate the F</etc/asterisk/users.conf> which is used by asterisk(1)
+and AsteriskGUI.
+
+Its location may be overriden via the environment variable F<USERS_FILE>.
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm
new file mode 100644
index 0000000..e8fd733
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zapata.pm
@@ -0,0 +1,213 @@
+package Zaptel::Config::Gen::Zapata;
+use strict;
+
+use Zaptel::Config::Gen qw(is_true);
+
+sub new($$$) {
+ my $pack = shift || die;
+ my $gconfig = shift || die;
+ my $genopts = shift || die;
+ my $file = $ENV{ZAPATA_FILE} || "/etc/asterisk/zapata-channels.conf";
+ my $self = {
+ FILE => $file,
+ GCONFIG => $gconfig,
+ GENOPTS => $genopts,
+ };
+ bless $self, $pack;
+ return $self;
+}
+
+# Since zapata definitions "leak" to the next ones, we try
+# To reset some important definitions to their chan_zap defaults.
+my %chan_zap_defaults = (
+ context => 'default',
+ group => '63', # FIXME: should not be needed.
+ overlapdial => 'no',
+ busydetect => 'no',
+ rxgain => 0,
+ txgain => 0,
+);
+
+sub reset_zapata_values {
+ foreach my $arg (@_) {
+ if (exists $chan_zap_defaults{$arg}) {
+ print "$arg = $chan_zap_defaults{$arg}\n";
+ } else {
+ print "$arg =\n";
+ }
+ }
+}
+
+sub gen_digital($$) {
+ my $self = shift || die;
+ my $span = shift || die;
+ my $gconfig = $self->{GCONFIG};
+ my $num = $span->num() || die;
+ die "Span #$num is analog" unless $span->is_digital();
+ if($span->is_pri && $gconfig->{'pri_connection_type'} eq 'R2') {
+ printf "; Skipped: $gconfig->{'pri_connection_type'}\n\n";
+ return;
+ }
+ my $type = $span->type() || die "$0: Span #$num -- unkown type\n";
+ my $termtype = $span->termtype() || die "$0: Span #$num -- unkown termtype [NT/TE]\n";
+ my $group = $gconfig->{'group'}{"$type"};
+ my $context = $gconfig->{'context'}{"$type"};
+ my @to_reset = qw/context group/;
+
+ die "$0: missing default group (termtype=$termtype)\n" unless defined($group);
+ die "$0: missing default context\n" unless $context;
+
+ my $sig = $span->signalling || die "missing signalling info for span #$num type $type";
+ grep($gconfig->{'bri_sig_style'} eq $_, 'bri', 'bri_ptmp', 'pri') or die "unknown signalling style for BRI";
+ if($span->is_bri() and $gconfig->{'bri_sig_style'} eq 'bri_ptmp') {
+ $sig .= '_ptmp';
+ }
+ if ($span->is_bri() && $termtype eq 'NT' && is_true($gconfig->{'brint_overlap'})) {
+ print "overlapdial = yes\n";
+ push(@to_reset, qw/overlapdial/);
+ }
+
+ $group .= "," . (10 + $num); # Invent unique group per span
+ printf "group=$group\n";
+ printf "context=$context\n";
+ printf "switchtype = %s\n", $span->switchtype;
+ printf "signalling = %s\n", $sig;
+ printf "channel => %s\n", Zaptel::Config::Gen::bchan_range($span);
+ reset_zapata_values(@to_reset);
+}
+
+sub gen_channel($$) {
+ my $self = shift || die;
+ my $chan = shift || die;
+ my $gconfig = $self->{GCONFIG};
+ my $type = $chan->type;
+ my $num = $chan->num;
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ my $exten = $gconfig->{'base_exten'} + $num;
+ my $sig = $gconfig->{'zapata_signalling'}{$type};
+ my $context = $gconfig->{'context'}{$type};
+ my $group = $gconfig->{'group'}{$type};
+ my $callerid;
+ my $immediate;
+
+ return if $type eq 'EMPTY';
+ die "missing default_zapata_signalling for chan #$num type $type" unless $sig;
+ $callerid = ($type eq 'FXO')
+ ? 'asreceived'
+ : sprintf "\"Channel %d\" <%04d>", $num, $exten;
+ if($type eq 'IN') {
+ $immediate = 'yes';
+ }
+ # FIXME: $immediage should not be set for 'OUT' channels, but meanwhile
+ # it's better to be compatible with genzaptelconf
+ $immediate = 'yes' if $gconfig->{'fxs_immediate'} eq 'yes' and $sig =~ /^fxo_/;
+ my $signalling = $chan->signalling;
+ $signalling = " " . $signalling if $signalling;
+ my $info = $chan->info;
+ $info = " " . $info if $info;
+ printf ";;; line=\"%d %s%s%s\"\n", $num, $chan->fqn, $signalling, $info;
+ printf "signalling=$sig\n";
+ printf "callerid=$callerid\n";
+ printf "mailbox=%04d\n", $exten unless $type eq 'FXO';
+ if(defined $group) {
+ printf "group=$group\n";
+ }
+ printf "context=$context\n";
+ printf "immediate=$immediate\n" if defined $immediate;
+ printf "channel => %d\n", $num;
+ # Reset following values to default
+ printf "callerid=\n";
+ printf "mailbox=\n" unless $type eq 'FXO';
+ if(defined $group) {
+ printf "group=\n";
+ }
+ printf "context=default\n";
+ printf "immediate=no\n" if defined $immediate;
+ print "\n";
+}
+
+sub generate($) {
+ my $self = shift || die;
+ my $file = $self->{FILE};
+ my $gconfig = $self->{GCONFIG};
+ my $genopts = $self->{GENOPTS};
+ #$gconfig->dump;
+ my @spans = @_;
+ warn "Empty configuration -- no spans\n" unless @spans;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ print "Generating $file\n" if $genopts->{verbose};
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ printf "; Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime);
+ print <<"HEAD";
+; Zaptel Channels Configurations (zapata.conf)
+;
+; This is not intended to be a complete zapata.conf. Rather, it is intended
+; to be #include-d by /etc/zapata.conf that will include the global settings
+;
+
+HEAD
+ foreach my $span (@spans) {
+ printf "; Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ if($span->is_digital()) {
+ $self->gen_digital($span);
+ } else {
+ foreach my $chan ($span->chans()) {
+ if(is_true($genopts->{'freepbx'}) || is_true($gconfig->{'freepbx'})) {
+ # Freepbx has its own idea about channels
+ my $type = $chan->type;
+ if($type eq 'FXS' || $type eq 'OUT' || $type eq 'IN') {
+ printf "; Skip channel=%s($type) -- freepbx option.\n",
+ $chan->num;
+ next;
+ }
+ }
+ $self->gen_channel($chan);
+ }
+ }
+ print "\n";
+ }
+ close F;
+ select $old;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+zapata - Generate configuration for zapata channels.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Gen::Zapata;
+
+ my $cfg = new Zaptel::Config::Gen::Zapata(\%global_config, \%genopts);
+ $cfg->generate(@span_list);
+
+=head1 DESCRIPTION
+
+Generate the F</etc/asterisk/zapata-channels.conf>
+This is used as a configuration for asterisk(1).
+It should be included in the main F</etc/asterisk/zapata.conf>.
+
+Its location may be overriden via the environment variable
+C<ZAPATA_FILE>.
+
+=head1 OPTIONS
+
+=over 4
+
+=item freepbx
+
+With this option we do not generate channel definitions for FXS, Input and
+Output ports. This is done because these channel definitions need to be
+generated and inserted into I<freepbx> database anyway.
+
+=back
+
+The I<freepbx> option may be activated also by adding a C<freepbx yes> line
+to the C<genconf_parameters> file.
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm
new file mode 100644
index 0000000..2c30c33
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Gen/Zaptel.pm
@@ -0,0 +1,158 @@
+package Zaptel::Config::Gen::Zaptel;
+use strict;
+
+use Zaptel::Config::Gen qw(is_true);
+
+sub new($$$) {
+ my $pack = shift || die;
+ my $gconfig = shift || die;
+ my $genopts = shift || die;
+ my $file = $ENV{ZAPCONF_FILE} || "/etc/zaptel.conf";
+ my $self = {
+ FILE => $file,
+ GCONFIG => $gconfig,
+ GENOPTS => $genopts,
+ };
+ bless $self, $pack;
+ return $self;
+}
+
+my $bri_te_last_timing = 1;
+
+sub gen_digital($$) {
+ my $gconfig = shift || die;
+ my $span = shift || die;
+ my $num = $span->num() || die;
+ die "Span #$num is analog" unless $span->is_digital();
+ my $termtype = $span->termtype() || die "$0: Span #$num -- unkown termtype [NT/TE]\n";
+ my $timing;
+ my $lbo = 0;
+ my $framing = $span->framing() || die "$0: No framing information for span #$num\n";
+ my $coding = $span->coding() || die "$0: No coding information for span #$num\n";
+ my $span_crc4 = $span->crc4();
+ $span_crc4 = (defined $span_crc4) ? ",$span_crc4" : '';
+ my $span_yellow = $span->yellow();
+ $span_yellow = (defined $span_yellow) ? ",$span_yellow" : '';
+ # "MFC/R2 does not normally use CRC4"
+ # FIXME: a finer way to override:
+ if ($gconfig->{'pri_connection_type'} eq 'R2') {
+ $span_crc4 = '';
+ $framing = 'cas';
+ }
+ my $dchan_type = 'dchan';
+ if ($span->is_bri() && is_true($gconfig->{'bri_hardhdlc'})) {
+ $dchan_type = 'hardhdlc';
+ }
+
+ $timing = ($termtype eq 'NT') ? 0 : $bri_te_last_timing++;
+ printf "span=%d,%d,%d,%s,%s%s%s\n",
+ $num,
+ $timing,
+ $lbo,
+ $framing,
+ $coding,
+ $span_crc4,
+ $span_yellow;
+ printf "# termtype: %s\n", lc($termtype);
+ if ($gconfig->{'pri_connection_type'} eq 'PRI') {
+ printf "bchan=%s\n", Zaptel::Config::Gen::bchan_range($span);
+ my $dchan = $span->dchan();
+ printf "$dchan_type=%d\n", $dchan->num();
+ } elsif ($gconfig->{'pri_connection_type'} eq 'R2' ) {
+ my $idle_bits = $gconfig->{'r2_idle_bits'};
+ printf "cas=%s:$idle_bits\n", Zaptel::Config::Gen::bchan_range($span);
+ printf "dchan=%d\n", $span->dchan()->num();
+ }
+}
+
+sub gen_signalling($$) {
+ my $gconfig = shift || die;
+ my $chan = shift || die;
+ my $type = $chan->type;
+ my $num = $chan->num;
+
+ die "channel $num type $type is not an analog channel\n" if $chan->span->is_digital();
+ if($type eq 'EMPTY') {
+ printf "# channel %d, %s, no module.\n", $num, $chan->fqn;
+ return;
+ }
+ my $signalling = $gconfig->{'zaptel_signalling'};
+ my $sig = $signalling->{$type} || die "unknown default zaptel signalling for chan $num type $type";
+ if ($type eq 'IN') {
+ printf "# astbanktype: input\n";
+ } elsif ($type eq 'OUT') {
+ printf "# astbanktype: output\n";
+ }
+ printf "$sig=$num\n";
+}
+
+sub generate($$$) {
+ my $self = shift || die;
+ my $file = $self->{FILE};
+ my $gconfig = $self->{GCONFIG};
+ my $genopts = $self->{GENOPTS};
+ my @spans = @_;
+ warn "Empty configuration -- no spans\n" unless @spans;
+ rename "$file", "$file.bak"
+ or $! == 2 # ENOENT (No dependency on Errno.pm)
+ or die "Failed to backup old config: $!\n";
+ #$gconfig->dump;
+ print "Generating $file\n" if $genopts->{verbose};
+ open(F, ">$file") || die "$0: Failed to open $file: $!\n";
+ my $old = select F;
+ printf "# Autogenerated by %s on %s -- do not hand edit\n", $0, scalar(localtime);
+ print <<"HEAD";
+# Zaptel Configuration File
+#
+# This file is parsed by the Zaptel Configurator, ztcfg
+#
+HEAD
+ foreach my $span (@spans) {
+ printf "# Span %d: %s %s\n", $span->num, $span->name, $span->description;
+ if($span->is_digital()) {
+ gen_digital($gconfig, $span);
+ } else {
+ foreach my $chan ($span->chans()) {
+ if(1 || !defined $chan->type) {
+ my $type = $chan->probe_type;
+ my $num = $chan->num;
+ die "Failed probing type for channel $num"
+ unless defined $type;
+ $chan->type($type);
+ }
+ gen_signalling($gconfig, $chan);
+ }
+ }
+ print "\n";
+ }
+ print <<"TAIL";
+# Global data
+
+loadzone = $gconfig->{'loadzone'}
+defaultzone = $gconfig->{'defaultzone'}
+TAIL
+ close F;
+ select $old;
+}
+
+1;
+
+__END__
+
+=head1 NAME
+
+zaptel - Generate configuration for zaptel drivers.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Gen::Zaptel;
+
+ my $cfg = new Zaptel::Config::Gen::Zaptel(\%global_config, \%genopts);
+ $cfg->generate(@span_list);
+
+=head1 DESCRIPTION
+
+Generate the F</etc/zaptel.conf>.
+This is the configuration for ztcfg(1).
+
+Its location may be overriden via the environment variable F<ZAPCONF_FILE>.
diff --git a/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm b/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm
new file mode 100644
index 0000000..7f6ae80
--- /dev/null
+++ b/kernel/xpp/utils/zconf/Zaptel/Config/Params.pm
@@ -0,0 +1,149 @@
+package Zaptel::Config::Params;
+#
+# Written by Oron Peled <oron@actcom.co.il>
+# Copyright (C) 2009, Xorcom
+# This program is free software; you can redistribute and/or
+# modify it under the same terms as Perl itself.
+#
+# $Id$
+#
+use strict;
+
+=head1 NAME
+
+Zaptel::Config::Params -- Object oriented representation of F<genconf_parameters> file.
+
+=head1 SYNOPSIS
+
+ use Zaptel::Config::Params;
+ my $params = Zaptel::Config::Params->new('the-config-file');
+ print $params->item{'some-key'};
+ $params->dump; # For debugging
+
+=head1 DESCRIPTION
+
+The constructor must be given a configuration file name:
+
+=over 4
+
+=item * Missing file is B<not> an error.
+
+=item * Other opening errors cause a C<die> to be thrown.
+
+=item * The file name is saved as the value of C<GENCONF_FILE> key.
+
+=back
+
+The access to config keys should only be done via the C<item()> method:
+
+=over 4
+
+=item * It contains all hard-coded defaults.
+
+=item * All these values are overriden by directives in the config file.
+
+=back
+
+=cut
+
+sub new($$) {
+ my $pack = shift || die;
+ my $cfg_file = shift || die;
+ my $self = {
+ GENCONF_FILE => $cfg_file,
+ };
+ bless $self, $pack;
+ if(!open(F, $cfg_file)) {
+ if(defined($!{ENOENT})) {
+ #print STDERR "No $cfg_file. Assume empty config\n";
+ return $self; # Empty configuration
+ }
+ die "$pack: Failed to open '$cfg_file': $!\n";
+ }
+ #print STDERR "$pack: $cfg_file\n";
+ my $array_key;
+ while(<F>) {
+ my ($key, $val);
+ chomp;
+ s/#.*$//;
+ s/\s+$//; # trim tail whitespace
+ next unless /\S/;
+ if(defined $array_key && /^\s+/) {
+ s/^\s+//; # trim beginning whitespace
+ push(@{$self->{$array_key}}, $_);
+ next;
+ }
+ undef $array_key;
+ ($key, $val) = split(/\s+/, $_, 2);
+ $key = lc($key);
+ if(! defined $val) {
+ $array_key = $key;
+ next;
+ }
+ die "$cfg_file:$.: Duplicate key '$key'\n", if exists $self->{$key};
+ $self->{$key} = $val;
+ }
+ close F;
+ return $self;
+}
+
+sub item($$) {
+ my $self = shift || die;
+ my $key = shift || die;
+ my %defaults = (
+ base_exten => '4000',
+ freepbx => 'no', # Better via -F command line
+ fxs_immediate => 'no',
+ fxs_default_start => 'ks',
+ fxo_default_start => 'ks',
+ lc_country => 'us',
+ context_lines => 'from-pstn',
+ context_phones => 'from-internal',
+ context_input => 'astbank-input',
+ context_output => 'astbank-output',
+ group_phones => '5',
+ group_lines => '0',
+ brint_overlap => 'no',
+ bri_sig_style => 'bri_ptmp',
+ echo_can => 'mg2',
+ bri_hardhdlc => 'no',
+ pri_connection_type => 'PRI',
+ r2_idle_bits => '1101',
+ 'pri_termtype' => [ 'SPAN/* TE' ],
+ );
+
+ return (exists($self->{$key})) ? $self->{$key} :$defaults{$key};
+}
+
+sub dump($) {
+ my $self = shift || die;
+ printf STDERR "%s dump:\n", ref $self;
+ my $width = 30;
+ foreach my $k (sort keys %$self) {
+ my $val = $self->{$k};
+ my $ref = ref $val;
+ #print STDERR "DEBUG: '$k', '$ref', '$val'\n";
+ if($ref eq '') {
+ printf STDERR "%-${width}s %s\n", $k, $val;
+ } elsif($ref eq 'SCALAR') {
+ printf STDERR "%-${width}s %s\n", $k, ${$val};
+ } elsif($ref eq 'ARRAY') {
+ #printf STDERR "%s:\n", $k;
+ my $i = 0;
+ foreach my $v (@{$val}) {
+ printf STDERR "%-${width}s %s\n", "$k\->[$i]", $v;
+ $i++;
+ }
+ } elsif($ref eq 'HASH') {
+ #printf STDERR "%s:\n", $k;
+ foreach my $k1 (keys %{$val}) {
+ printf STDERR "%-${width}s %s\n", "$k\->\{$k1\}", ${$val}{$k1};
+ }
+ } else {
+ printf STDERR "%-${width}s (-> %s)\n", $k, $ref;
+ }
+ }
+}
+
+1;
+