#! /bin/bash

# genzaptelconf: generate as smartly as you can:
#		/etc/zaptel.conf
#		/etc/asterisk/zapata-channels.conf (to be #include-d into zapata.conf)
#	update:
#		With '-M' /etc/modules (list of modules to load)
#
# Copyright (C) 2005 by Xorcom <support@xorcom.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
#
#
# If you have any technical questions, contact 
# Tzafrir Cohen <tzafrir.cohen@xorcom.com>
#	

# The script uses a number of bash-specific features
# TODO: either ditch them or convert to perl
# Don't override variables here. 
# Override them in /etc/default/zaptel (debian) or /etc/sysconfig/zaptel 
# (redhat/centos)

# /etc/default/zaptel may override the following variables
VERSION=0.5.7
rcsid='$Id: genzaptelconf 1559 2006-11-06 20:09:45Z tzafrir $'
lc_country=us
base_exten=6000
# If set: no context changes are made in zapata-channels.conf
#context_manual=yes
context_lines=from-pstn      # context into which PSTN calls go
context_phones=from-internal # context for internal phones calls.
# The two below apply to input and output ports of the Xorcom Astribank:
context_input=astbank-input
context_output=astbank-output # useless, but helps marking the channels :-)
# TODO: what about PRI/BRI?
# If set: no group changes are made in zapata-channels.conf
#group_manual=yes
group_phones=5 # group for phones
group_lines=0  # group for lines
# set 'immediate=yes' for Asteribank input channels and 'immediate=no' 
# for others. Note that if an Astribank is not detected, the script 
# will set this to "no", so you can safely leave it as "yes".
set_immediate=yes
# Set fxs_immediate to 'yes' to make all FXS lines answer immediately.
fxs_immediate=no

ZAPCONF_FILE=${ZAPCONF_FILE:-/etc/zaptel.conf}
ZAPCONF_FILE_SYSTEM=$ZAPCONF_FILE
ZAPATA_FILE=${ZAPATA_FILE:-/etc/asterisk/zapata-channels.conf}
ZAPTEL_BOOT_DEBIAN=${ZAPTEL_BOOT_DEBIAN:-/etc/default/zaptel}
ZAPTEL_BOOT_FEDORA=${ZAPTEL_BOOT_FEDORA:-/etc/sysconfig/zaptel}
MODLIST_FILE=/etc/modules
MODLIST_FILE_FEDORA=/etc/sysconfig/zaptel
exten_base_dir=/etc/asterisk/extensions-phones.d
exten_defs_file=/etc/asterisk/extensions-defs.conf
# how long to wait for /dev/zap/ctl to appear? (seconds)
DEVZAP_TIMEOUT=${DEVZAP_TIMEOUT:-20}
ZTCFG=${ZTCFG:-/sbin/ztcfg}

# a temporary directory to store whatever we need to remember.
#
# The main loop of genconf is run in a sub-process.
tmp_dir=

# A list of all modules:
# - the list of modules which will be probed (in this order) if -d is used
# - The module that will be deleted from /etc/modules , if -d -M is used
ALL_MODULES="zaphfc qozap ztgsm wctdm wctdm24xxp wcfxo wcfxs pciradio tor2 torisa wct1xxp wct4xxp wcte11xp wanpipe wcusb xpp_usb"

# read default configuration from /etc/default/zaptel
if [ -r $ZAPTEL_BOOT_DEBIAN ]; then . $ZAPTEL_BOOT_DEBIAN; fi
if [ -r $ZAPTEL_BOOT_FEDORA ]; then . $ZAPTEL_BOOT_FEDORA; fi

if [ ! -x "$ZTCFG" ]; then
	# Work around a bug in the rpm package: ztcfg should be in 
	# /sbin as it may be required for starting a network interface
	if [ -x /usr/sbin/ztcfg ]; then
		ZTCFG=/usr/sbin/ztcfg
	else
		echo >&2 "ztcfg is not on found, do you have zaptel properly installed?"
		exit 1;
	fi
fi

# it is safe to use -c twice: the last one will be used.
ztcfg_cmd="$ZTCFG -c $ZAPCONF_FILE"

# work around a bug (that was already fixed) in the installer:
if [ "$lc_country" = '' ]; then lc_country=us; fi

force_stop_ast=no
do_detect=no
do_unload=no
do_module_list=no
verbose=no
do_restart=yes
fxsdisable=no

span_te_timing_counter=1

die() {
	echo "$@" >&2
	exit 1
}

say() {
	if [ "$verbose" = no ]; then
		return
	fi
	echo "$@"   >&2
}

# Wait for udev to generate /dev/zap/ctl, if needed:
wait_for_zapctl() {
	# if device file already exists, or if zaptel has failed to load: 
	# no point waiting.
	if [ -c /dev/zap/ctl ] || ! grep -q zaptel /proc/modules ; then
	  return
  fi
  say "Waiting for /dev/zap/ctl to be generated"
  devzap_found=0
  for i in `seq $DEVZAP_TIMEOUT`; do
		sleep 1
		if [ -c /dev/zap/ctl ]; then
		  devzap_found=1
			break
		fi
	done
	if [ "$devzap_found" != 1 ]; then
	  say "Still no /dev/zap/ctl after $devzap_timeout seconds."
	  echo >&2 "No /dev/zap/ctl: cannot run ztcfg. Aborting."
	fi
}

run_ztcfg() {
	# Run ztcfg itself
	if [ "$verbose" = no ]; then
		$ztcfg_cmd "$@"
	else
		say "Reconfiguring identified channels"
		$ztcfg_cmd -vv "$@"
	fi
}

update_module_list_debian() {
	say "Updating Debian modules list $MODLIST_FILE."
	del_args=`for i in $ALL_MODULES ztdummy
	do
		echo "$i" | sed s:.\*:-e\ '/^&/d':
	done`
	add_args=`for i in $*
	do
		echo "$i" | sed s:.\*:-e\ '\$a&':
	done`
	
	sed -i.bak $del_args "$MODLIST_FILE"
	for i in $*
	do
		echo "$i"
	done >> "$MODLIST_FILE"
}

update_module_list_fedora() {
	say "Updating modules list in zaptel init config $MODLIST_FILE_FEDORA."
	sed -i.bak -e '/^MODULES=/d' "$MODLIST_FILE_FEDORA"
	echo "MODULES=\"$*\"" >> "$MODLIST_FILE_FEDORA"
}

update_module_list() {
  if   [ -f "$MODLIST_FILE" ]; then
	  update_module_list_debian "$@"
	elif [ -f "$MODLIST_FILE_FEDORA" ]; then
	  update_module_list_fedora "$@"
	else
	  die "Can't find a modules list to update. Tried: $MODLIST_FILE, $MODLIST_FILE_FEDORA. Aborting"
	fi
}



zap_reg_xpp() {
	if [ ! -d /proc/xpp ]; then return; fi

	# Get a list of connected Astribank devices, sorted by the name of
	# the USB connector. That order is rather arbitrary, but will not
	# change without changes to the cabling.
	xbusses=`sort -k 2 /proc/xpp/xbuses | awk -F: '/STATUS=connected/ {print $1}'`

	# get a list of XPDs that were not yet registered as zaptel spans.
	# this will be the case if you set the parameter zap_autoreg=0 to
	# the module xpp
	# Append /dev/null to provide a valid file name in case of an empty pattern.
	xbusses_pattern=`echo $xbusses| sed -e 's|XBUS-[0-9]*|/proc/xpp/&/XPD-*/zt_registration|g'`' /dev/null'
	xpds_to_register=`grep -l 0 $xbusses_pattern`
	for file in $xpds_to_register; do
		echo 1 >$file
	done
}


check_for_astribank(){
	if ! grep -q XPP_IN/ /proc/zaptel/* 2>/dev/null
	then
		# we only get here is if we find no Astribank input channels
		# in /proc/zaptel . Hence we can safely disable their special settings:
		set_immediate=no
	fi
}

usage() {
	program=`basename $0`

	echo >&2 "$program: generate zaptel.conf and zapata.conf"
	echo >&2 "(version $VERSION, $rcsid)"
	echo >&2 "usage:"
	echo >&2 " $program [-sRdv] [-m k|l|g] [-c <country_code>] [-e <base_exten>] [-F]"
	echo >&2 " $program [-sRdv] -l"
	echo >&2 " $program -su"
	echo >&2 " $program -h (this screen)"
	echo >&2 ""
	echo >&2 "Options:"
	echo >&2 "  -c CODE: set the country code (default: $lc_country)"
	echo >&2 "  -e NUM: set the base extension number (default: $base_exten)"
	echo >&2 "  -F: Don't print FXSs in zapata.conf"
	echo >&2 "  -l: output a list of detected channels instead of zaptel.conf"
	echo >&2 "  -d: Perform hardware detection"
	echo >&2 "  -u: Unload zaptel modules (will not restart Asterisk)."
	echo >&2 "  -v: verbose"
	echo >&2 "  -s: Stop Asterisk before running, and start it at the end."
	echo >&2 "  -R: Don't restart asterisk in the end."
}

# $1: channel number
print_pattern() {
	local astbank_type=''
	OPTIND=1
	while getopts 'a:' arg
	do
		case "$arg" in
			a) case "$OPTARG" in input|output) astbank_type=$OPTARG;;esac ;;
		esac
	done
	shift $(( $OPTIND-1 ))


	local chan=$1
	local sig=$2 #fxs/fxo
	local mode=$3
	local method='ks'
	if [ "$lc_country" = il ] && [ "$sig" = 'fxs' ]
	then method=ls
	fi
	case "$mode" in
	list) echo $chan $sig $astbanktype;;
	files)
		# sadly, both input ports and output ports go into the same span as 
		# the FXS ports. Thus we need to separate between them. See also 
		# the zapata.conf section:
		if [ "$astbank_type" != '' ]; 
			then echo "# astbanktype: $astbank_type" >>$zaptel_file; 
		fi
		echo "${sig}$method=$chan" >>$zaptel_file
		# zap2amp will rewrite those from zaptel.conf and hints there
		if [ "$fxsdisable" = 'yes' ] && [ "$sig" = 'fxo' ]; then return; fi
			
		echo "signalling=${sig}_$method" >>$zapata_file
		if [ "$sig" = 'fxo' ]
		then
			# to preconfigure channel 1's extension to 550, set
			# chan_1_exten=550
			# in, e.g, /etc/default/zaptel
		  var_name=`echo chan_${chan}_exten`
			cfg_exten=`echo ${!var_name} | tr -d -c 0-9`
		  var_name=`echo chan_${chan}_vmbox`
			cfg_vmbox=`echo ${!var_name} | tr -d -c 0-9`
		  var_name=`echo chan_${chan}_cntxt`
			cfg_cntxt=`echo ${!var_name} | tr -d -c 0-9`
			
			if [ "$cfg_exten" = '' ]
			then # No extension number set for this channel
				exten=$(($chan+$base_exten))
			else # use the pre-configured extension number
				exten=$cfg_exten
			fi
			# is there any real need to set 'mailbox=' ?
			if [ "x$cfg_vmbox" = x ]
			then # No extension number set for this channel
				vmbox=$exten
			else # use the pre-configured extension number
				vmbox=$cfg_vmbox
			fi
			echo "callerid=\"Channel $chan\" <$exten>"     >> $zapata_file
			echo "mailbox=$exten"                          >> $zapata_file
			if [ "$group_manual" != "yes" ]
			then 
				echo "group=$group_phones"                   >> $zapata_file
			fi
			if [ "$context_manual" != "yes" ]
			then
				if [ "$astbank_type" != '' ];
				then 
					context_var_name=context_$astbank_type
					echo context=${!context_var_name}          >> $zapata_file
				else
					echo "context=$context_phones"             >> $zapata_file
				fi
			fi
		else # this is an FXO (trunk/phone: FXO signalling)
		  # we have may have set it. So reset it:
			echo "callerid=asreceived"                     >> $zapata_file
			echo "mailbox="                                >> $zapata_file
			if [ "$group_manual" != "yes" ]
			then 
				echo "group=$group_lines"                    >> $zapata_file
			fi
			if [ "$context_manual" != "yes" ]
			then 
				echo "context=$context_lines"                >> $zapata_file
			fi
			if [ "$lc_country" = 'uk' ]
			then
			  echo "cidsignalling=v23"                     >> $zapata_file
			  case $line in 
			  *WCFXO*) echo "cidstart=history"             >> $zapata_file;;
			  *)       echo "cidstart=polarity"            >> $zapata_file;;
			  esac
			fi
			echo ";;; line=\"$line\""                      >> $zapata_file
			# if kewlstart is not used, busydetect has to be employed:
			if [ "$method" = 'ls' ]
			then echo 'busydetect=yes'                     >> $zapata_file
			else echo 'busydetect=no'                      >> $zapata_file
			fi
		fi

		if [ "$set_immediate" = 'yes' ]
		then
			if [ "$astbank_type" = 'input' ] || \
				( [ "$fxs_immediate" = 'yes' ] && [ "$sig" = "fxo" ] )
			then echo 'immediate=yes'                      >> $zapata_file
			else echo 'immediate=no'                       >> $zapata_file
			fi
		fi
		echo "channel => $chan"                          >> $zapata_file
		echo ""                                          >> $zapata_file

		# Keep a note of what channels we have identified
		say "DEBUG: adding to channels list: channel: $chan, sig: $sig"
		case "$sig" in
		fxs)
			echo $chan >$tmp_dir/fxo_$chan
			say "DEBUG: FXO list now contains: `cat $tmp_dir/fxo_* |xargs`"
			;;
		fxo)
			echo $chan >$tmp_dir/fxs_$chan
			say "DEBUG: FXS list now contains: `cat $tmp_dir/fxs_* |xargs`"
			;;
		esac
		;;
	esac
	
}

# the number of channels from /proc/zaptel
# must always print a number as its output.
count_proc_zap_lines() {
	# if zaptel is not loaded there are 0 channels:
	if [ ! -d /proc/zaptel ]; then echo '0'; return; fi
	
	(
		for file in `echo /proc/zaptel/* |grep -v '\*'`
		do sed -e 1,2d $file # remove the two header lines
		done
	) | wc -l # the total number of lines
}

load_modules() {
	say "Test Loading modules:"
	for i in $ALL_MODULES
	do
		lines_before=`count_proc_zap_lines`
		args="${i}_args"
		eval "args=\$$args"
		# a module is worth listing if it:
		# a. loaded successfully, and
		# b. added channels lines under /proc/zaptel/*
		if /sbin/modprobe $i $args 2> /dev/null 
		then
		  check=0
		  case "$i" in
			xpp_usb) check=`grep 'STATUS=connected' 2>/dev/null /proc/xpp/xbuses | wc -l` ;;
			*) if [ $lines_before -lt `count_proc_zap_lines` ]; then check=1; fi ;;
			esac
			if [ "$check" != 0 ]
			then
			  probed_modules="$probed_modules $i"
			  say "	ok	$i	$args"
		  else
			  say "	- 	$i	$args"
				rmmod $i
		  fi
		else
			say "	- 	$i	$args"
		fi
	done
}

# recursively unload a module and its dependencies, if possible.
# where's modprobe -r when you need it?
# inputs: module to unload.
# returns: the result from 
unload_module() {
	module="$1"
	line=`lsmod 2>/dev/null | grep "^$1 "`
	if [ "$line" = '' ]; then return; fi # module was not loaded

	set -- $line
	# $1: the original module, $2: size, $3: refcount, $4: deps list
	mods=`echo $4 | tr , ' '`
	# old versions of xpd_fxs actually depend on xpp, but forget to tell it.
	# bug has already been fixed but the code will remain here for a while
	# just in case
	case "$module" in xpd_*) mods="xpp_usb $mods";; esac
	for mod in $mods; do
		# run in a subshell, so it won't step over our vars:
		(unload_module $mod) 
		# TODO: the following is probably the error handling we want:
		# if [ $? != 0 ]; then return 1; fi
	done
	rmmod $module
}

unload_modules() {
	if
		pids="$(pgrep asterisk)"
		[ "$pids" != '' ]
	then
		die "Before unloading -- STOP asterisk (pids=$pids)."
	fi
	say "Unloading zaptel modules:"
	unload_module zaptel
	say ''
}

# sleep a while until the xpp modules fully register
wait_for_xpp() {
	if [ -d /proc/xpp ]
	then
		# wait for the XPDs to register:
		# TODO: improve error reporting and produce a messagee here
		cat /proc/xpp/XBUS-*/waitfor_xpds 2>/dev/null >/dev/null  || true
	fi
}

detect() {
	unload_modules
	temporary_zapconf save
	load_modules
	temporary_zapconf restore
	modlist="$probed_modules"
	modlist="$(echo $modlist)"		# clean spaces
	if [ "$do_module_list" = yes ]
	then
		update_module_list "$modlist"
	fi
	if echo $modlist | grep -q xpp_usb; then wait_for_xpp; fi
}

# The module configuration generated by zaptel includes a totally useless 
# automatic run of ztcfg after modules loading. As a workaround for that, 
# we provide an empty zaptel.conf temporarily.
# 
# At hardware detection time we shouldn't really touch zaptel.conf . So we 
# must keep a copy of it somewhere.
#
# I use ZAPCONF_FILE_SYSTEM rather than ZAPCONF_FILE, as the bogus modprobe 
# entries will not be initiated by us.
#
# ZAPCONF_FILE_TMP is a "global", as it needs to be preserved between the
# call to 'save' and to 'restore'
ZAPCONF_FILE_TMP=
temporary_zapconf() {
  case "$1" in
	save)
	  say "Temporarily moving zaptel.conf aside to work around broken modprobe.conf"
		ZAPCONF_FILE_TMP=`mktemp -t zaptel.conf-XXXXXX` || die "Error creating temporary zaptel.conf"
		cp -a $ZAPCONF_FILE_SYSTEM $ZAPCONF_FILE_TMP
		echo -n >$ZAPCONF_FILE_SYSTEM
		;;
	restore)
		# restore original zaptel.conf:
		if [ "$ZAPCONF_FILE_TMP" = '' ]; then return; fi
		mv $ZAPCONF_FILE_TMP $ZAPCONF_FILE_SYSTEM
		ZAPCONF_FILE_TMP=''
		;;
	esac
}

# TODO: kill this function. It is now unreferenced from anywhere.
check_tdm_sigtype() {
	chan_num=$1
	sig_type=$2
	mode=$3
	
	case "$sig_type" in
	fxs)chan_sig_type=fxo;;
	fxo)chan_sig_type=fxs;;
	esac

#	print_pattern $chan_num $chan_sig_type $mode
	
  # if you get syntax error from this line, make sure you use 'bash' 
  # rather than 'sh'
	$ztcfg_cmd -c <(print_pattern $chan_num $chan_sig_type zaptel) 2>/dev/null  \
		|| return 1
	if head -c1 /dev/zap/$chan_num >/dev/null 2>/dev/null
	then 
		print_pattern $chan_num $chan_sig_type $mode
		return 0
	else
		return 1
	fi	
}

genconf() {
	local mode=$1
	# reset FXO list (global)
	#say "DEBUG: resetting channels lists"
	rm -f $tmp_dir/fx{s,o}_*

# 	spanlist=`echo /proc/zaptel/* |  grep -v '\*'`
# 	spanlist=$(for i in `for i in  /proc/zaptel/*; do if [ -f $i ]; then echo $i |  cut -f 4 -d / ; fi; done | sort -n`; do echo -n "/proc/zaptel/$i "; done)
# 	spanlist=(cd /proc/zaptel; ls | sort -n | sed 's|^|/proc/zaptel/|')
	spanlist=`ls /proc/zaptel/ 2>/dev/null | sort -n | sed 's|^|/proc/zaptel/|'`

	#if [ "$spanlist" == "" ]; then
	#	die "No zapata interfaces in /proc/zaptel"
	#fi


	case "$mode" in 
		files)
			local zaptel_file=`mktemp -t genzaptelconf-zaptel-XXXXXX`
			local zapata_file=`mktemp -t genzaptelconf-zapata-XXXXXX`
			if [ -r "${ZAPCONF_FILE}" ]; then
				chown --reference="${ZAPCONF_FILE}" $zaptel_file || true
				chmod --reference="${ZAPCONF_FILE}" $zaptel_file 
			else
				chmod 644 $zaptel_file 
			fi
			if [ -r "${ZAPATA_FILE}" ]; then
				chown --reference="${ZAPATA_FILE}" $zapata_file || true
				chmod --reference="${ZAPATA_FILE}" $zapata_file 
			else
				chmod 644 $zapata_file 
			fi
			cat <<EOF >$zaptel_file
# Autogenerated by $0 -- do not hand edit
# Zaptel Configuration File
#
# This file is parsed by the Zaptel Configurator, ztcfg
#

# It must be in the module loading order

EOF
			cat <<EOF >$zapata_file
; Autogenerated by $0 -- do not hand edit
; 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
;
EOF
		;;
	esac

	# For each line in the spanlist: see if it represents a channel.
	# if it does, test that the channel is usable.
	# we do that by configuring it (using ztcfg with a 1-line config file)
	# and then trying to read 1 byte from the device file.
	#
	# The '<(command)' syntax creates a temporary file whose content is is the
	# output of 'command'.
	#
	# Another problem with such an approach is how to include an existing 
	# configuration file. For instance: how to include some default settings.
	#
	# Maybe an 'include' directive should be added to zaptel.conf ?
	#cat $spanlist | 
	for procfile in $spanlist
	do
		span_num=`basename $procfile`
		# the first line is the title line. It states the model name
		# the second line is empty
		title=`head -n 1 $procfile`
		# stuff that needs to be remembered accross lines (for PRI/BRI support)
		echo ""         >>$zaptel_file
		echo "# $title" >>$zaptel_file
		echo ""         >>$zapata_file
		echo "; $title" >>$zapata_file
		echo '-1'  >$tmp_dir/span_begin
		echo '-1'  >$tmp_dir/span_end
		echo '0'   >$tmp_dir/span_timing
		echo '1'   >$tmp_dir/span_lbo
		echo ''    >$tmp_dir/span_framing
		echo 'ami' >$tmp_dir/span_coding
		echo ''    >$tmp_dir/span_switchtype
		echo ''    >$tmp_dir/span_signalling

		# Check if ZapBRI cards are in TE or NT mode
		if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[NT\]\ |octoBRI \[NT\] |HFC-S PCI A ISDN.* \[NT\] )'
		then
			echo 'nt' >$tmp_dir/span_termtype
		else 
			if echo $title | egrep -q '((quad|octo)BRI PCI ISDN Card.* \[TE\]\ |octoBRI \[TE\] |HFC-S PCI A ISDN.* \[TE\] )'
			then
				echo 'te' >$tmp_dir/span_termtype
			fi
		fi
		# The rest of the lines are per-channel lines
		sed -e 1,2d $procfile | \
		while read line
		do 
			# in case this is a real channel. 
			chan_num=`echo $line |awk '{print $1}'`
			case "$line" in
			*WCTDM/*|*/WRTDM/*|*OPVXA1200/*) 
				# TDM400P/2400P and similar cards (Sangoma A200, OpenVox A1200)
				# this can be either FXS or FXO
				maybe_fxs=0
				maybe_fxo=0
				$ztcfg_cmd -c <(echo fxoks=$chan_num) &>/dev/null && maybe_fxs=1
				$ztcfg_cmd -c <(echo fxsks=$chan_num) &>/dev/null && maybe_fxo=1
				if [ $maybe_fxs = 1 ] && [ $maybe_fxo = 1 ]
				then 
				  # An installed module won't accept both FXS and FXO signalling types:
					# this is an empty slot.
					# TODO: I believe that the Sangoma A20x will reject both and thus 
					# print nothing here. 
				  echo "# channel $chan_num, WCTDM, no module." >> $zaptel_file
					continue
				fi
				
				if [ $maybe_fxs = 1 ]; then print_pattern $chan_num fxo $mode; fi
				if [ $maybe_fxo = 1 ]; then print_pattern $chan_num fxs $mode; fi
				;;
			*WCFXO/*) 
				# X100P
				print_pattern $chan_num fxs $mode || \
				echo "# channel $chan_num, WCFXO, inactive." >>$zaptel_file
				;;
			*WCUSB/*)
				print_pattern $chan_num fxo $mode
				;;
			*XPP_FXO/*)
				# Astribank FXO span
				print_pattern $chan_num fxs $mode
				;;
			*XPP_FXS/*)
				# Astribank FXS span (regular port)
				print_pattern $chan_num fxo $mode
				;;
			*XPP_OUT/*)
				# Astribank FXS span (output port)
				print_pattern -a output $chan_num fxo $mode
				;;
			*XPP_IN/*)
				# Astribank FXS span (input port)
				print_pattern -a input $chan_num fxo $mode
				;;
			*ZTHFC*/*|*ztqoz*/*|*ztgsm/*|*TE[24]/*|*WCT1/*|*Tor2/*|*TorISA/*|*XPP_BRI_*/*|*WP[TE]1/*)
				# PRI/BRI channel
				# Rather than identifying cards by the header line, we identify them by the channel names
				# This is shorter. This also allows us to count the channel numbers and check if a PRI
				# card is E1 or T1.
				if [ "`cat $tmp_dir/span_begin`" = "-1" ]
				then
					echo $chan_num      >$tmp_dir/span_begin
					echo $span_num      >$tmp_dir/span_num
					# The Astribank channels provide the information of TE/NT in the channel name. So 
					# why not use it?
					case "$line" in
					*XPP_BRI_TE/*) echo 'te' >$tmp_dir/span_termtype;;
					*XPP_BRI_NT/*) echo 'nt' >$tmp_dir/span_termtype;;
					esac
					case "$line" in
					*ZTHFC*/*|*ztqoz*/*|*XPP_BRI_*/*)
						# BRI channel
						echo 'ccs'          >$tmp_dir/span_framing
						echo 'euroisdn'     >$tmp_dir/span_switchtype
						if [ "`cat $tmp_dir/span_termtype`" = 'nt' 2>/dev/null ]
						then
							echo 'bri_net' >$tmp_dir/span_signalling 
						else
							echo 'bri_cpe' >$tmp_dir/span_signalling
						fi
						;;
					*ztgsm*/*)
						# Junghanns's GSM cards. 
						echo 'ccs'          >$tmp_dir/span_framing
            # what switch type? Any meaning to it?
						echo 'gsm'          >$tmp_dir/span_signalling
						;;
					*TE[24]/*|*WCT1/*|*Tor2/*|*TorISA/*|*WP[TE]1/*)
						# PRI span (E1/T1)
						echo 'esf'       >$tmp_dir/span_framing
						echo 'b8zs'      >$tmp_dir/span_coding
						echo 'national'  >$tmp_dir/span_switchtype
						echo 'pri_cpe'   >$tmp_dir/span_signalling
						# an example of country-specific setup. This is probably not accurate
						# Contributions are welcome
						case "$lc_country" in 
						nl)
							# (Just an example for per-country info)
							echo 'ccs'       >$tmp_dir/span_framing
							echo 'ami'       >$tmp_dir/span_coding
							#echo 'crc4'      >$tmp_dir/span_yellow
							#echo 'euroisdn'  >$tmp_dir/span_switchtype
							#echo 'pri_cpe'   >$tmp_dir/span_signalling
							;;
						il|de|au)
							echo 'ccs'       >$tmp_dir/span_framing
							echo 'hdb3'      >$tmp_dir/span_coding
							echo 'crc4'      >$tmp_dir/span_yellow
							echo 'euroisdn'  >$tmp_dir/span_switchtype
							;;
						cl)
							echo 'ccs'       >$tmp_dir/span_framing
							echo 'hdb3'      >$tmp_dir/span_coding
							#echo 'crc4'      >$tmp_dir/span_yellow
							echo 'national'  >$tmp_dir/span_switchtype
							;;
						esac
					;;
					esac
				fi
				# span_lastd is always the one before last 
				# channel. span_bchan is the last:
				echo $chan_num      >$tmp_dir/span_end
				;;
			'') ;;		# Empty line (after span header)
			*) 
				case "$mode" in
				list) echo "# ??: $line";;
				files)
					echo "# ??: $line" >>$zaptel_file
					echo "; ??: $line" >>$zaptel_file
				esac
				;;
			esac
		done
		# end of part in sub-process.
		
		if [ "`cat $tmp_dir/span_begin`" != -1 ]
		then # write PRI span ocnfig:
			# read files to variables:
			for suffix in num begin end timing lbo framing \
				coding switchtype signalling yellow termtype
			do
				eval span_$suffix=`cat $tmp_dir/span_$suffix 2>/dev/null`
			done
			if [ "$span_yellow" != '' ]; then span_yellow=",$span_yellow"; fi
			# exactly the same logic is used in asterisk's chan_zap.c.
			# also not that $(( )) is bash-specific
			case "$((1+ $span_end - $span_begin))" in
			2|3|24) #ztgsm, BRI or T1
			  dchan=$span_end
				bchans="$span_begin-$(($span_end-1))"
				;;
			31) #E1
				dchan="$(($span_begin+15))"
				bchans="$span_begin-$(($span_begin+14)),$(($span_begin+16))-$span_end"
				if [ "$span_switchtype" = 'national' ]; then
					span_framing=ccs
					span_coding=hdb3
					span_switchtype=euroisdn
				fi
				;;
			esac
			# Let's assume that a TE span should get the clock from the remote unit,
			# and NT spans should provide timing. Just as a sane default.
			# If we have several TE spans, the first will have priority 1, 
			# second: 2, etc.
			case "$span_signalling" in *_cpe*)
				span_timing=$span_te_timing_counter
				span_te_timing_counter=$(($span_te_timing_counter + 1))
				;;
			esac
			case "$mode" in
			files)
				echo span=$span_num,$span_timing,$span_lbo,$span_framing,$span_coding$span_yellow >> $zaptel_file
				if [ "$span_termtype" != '' ]
				then echo "# termtype: $span_termtype" >>$zaptel_file
				fi
				echo bchan=$bchans >>$zaptel_file
				echo dchan=$dchan  >>$zaptel_file
				
				if [ "$span_termtype" != '' ]
				then
					# an ISDN card's span that we know if it is in NT mode or TE mode.
					# NT is the same as FXS for us and TE is the same as FXO
					if [ "$span_termtype" = 'nt' ]
					then
						echo "callerid=\"Channels $span_begin - $span_end\" <$span_begin>" >> $zapata_file
						#echo "mailbox=$exten"
						if [ "$group_manual" != "yes" ]
						then 
							echo "group=$group_phones"     >> $zapata_file
						fi
						if [ "$context_manual" != "yes" ]
						then 
							echo "context=$context_phones" >> $zapata_file
						fi
					else # we have may have set it. So reset it:
						echo "callerid=asreceived"
						#echo "mailbox="
						if [ "$group_manual" != "yes" ]
						then 
							echo "group=$group_lines"      >> $zapata_file
						fi
						if [ "$context_manual" != "yes" ]
						then 
							echo "context=$context_lines"  >> $zapata_file
						fi
					fi
				fi
				echo "switchtype = $span_switchtype" >> $zapata_file
				echo "signalling = $span_signalling" >> $zapata_file
				echo "channel => $bchans"            >> $zapata_file
				;;
			list)
				echo BRI/PRI: chans: $bchans, control: $dchan
				;;
			esac
		fi
	done

	if [ "$mode" = 'files' ]
	then
		cat <<EOF >> ${zaptel_file}

# Global data

loadzone	= $loadzone
defaultzone	= $defaultzone
EOF
	fi
	
	mv ${ZAPCONF_FILE} ${ZAPCONF_FILE}.bak
	mv $zaptel_file ${ZAPCONF_FILE}
	mv ${ZAPATA_FILE} ${ZAPATA_FILE}.bak
	mv $zapata_file ${ZAPATA_FILE}
	# cleaning up the temp dir
	if [ -d "$tmp_dir" ]; then rm -rf "$tmp_dir"; fi
}

while getopts 'c:de:Fhlm:MRsuv' arg
do
	case "$arg" in
		e) # guarantee that it is a number:
			new_base_exten=`echo $OPTARG | tr -d -c 0-9`
			if [ "x$new_base_exten" != x ]; then base_exten=$new_base_exten; fi
			;;
		c) lc_country=`echo $OPTARG | tr -d -c a-z` ;;
		d) do_detect=yes ;;
		F) fxsdisable=yes;;
		u) do_unload=yes ;;
		v) verbose=yes ;;
		l) mode='list' ;;
		M) do_module_list=yes; do_detect=yes ;;
		s) force_stop_ast=yes ;;
		R) do_restart=no ;;
		h) usage; exit 0;;
		*) echo >&2 "unknown parameter -$arg, Aborting"; usage; exit 1;;
	esac
done
shift $(( $OPTIND-1 ))
if [ $# != 0 ]; then
	echo >&2 "$0: too many parameters"
	usage
	exit 1
fi

tmp_dir=`mktemp -d -t` || \
	die "$0: failed to create temporary directory. Aborting"


case "$lc_country" in
	# the list was generated from the source of zaptel:
	#grep '{.*[0-9]\+,.*"[a-z][a-z]"' zonedata.c | cut -d'"' -f 2 | xargs |tr ' ' '|'
	us|au|fr|nl|uk|fi|es|jp|no|at|nz|it|gr|tw|cl|se|be|sg|il|br|hu|lt|pl|za|pt|ee|mx|in|de|ch|dk|cz|cn):;;
	*) 
		lc_country=us
		echo >&2 "unknown country-code $lc_country, defaulting to \"us\""
		;;
esac
# any reason for loadzone and defaultzone to be different? If so, this is
# the place to make that difference
loadzone=$lc_country
defaultzone=$loadzone

# make sure asterisk is not in our way
if [ "$force_stop_ast" = 'yes' ]
then
  /etc/init.d/asterisk stop 1>&2
else
  # if asterisk is running and we wanted to detect modules
	# or simply to unload modules, asterisk needs to go away.
	if ( [ "$do_unload" = yes ] || [ "$do_detect" = yes ] ) && \
	  pidof asterisk >/dev/null 
	then
	  echo >&2 "Asterisk is already running. Configuration left untouched"
		echo >&2 "You can use the option -s to shut down Asterisk for the"
		echo >&2 "duration of the detection."
		exit 1
	fi
fi

if [ "$do_unload" = yes ]
then
	unload_modules
	exit
fi

if [ "$do_detect" = yes ]
then
	detect
fi

if [ "$mode" = list ]; then
	genconf list
else
	zap_reg_xpp
	check_for_astribank
	wait_for_zapctl
	say "Generating '${ZAPCONF_FILE} and ${ZAPATA_FILE}'"
	genconf files
	if [ "$set_immediate" = 'yes' ] && [ -x /etc/init.d/zaptel ]
	then /etc/init.d/zaptel start
	else run_ztcfg
	fi
fi

if [ "$tmp_dir" != '' ]
then
  rm -rf "$tmp_dir"
fi

if [ "$force_stop_ast" != 'yes' ] || [ "$do_restart" != 'yes' ]
then
  exit 0
fi

if [ -x /etc/init.d/asterisk ]
then
  /etc/init.d/asterisk start 1>&2
fi

# if in verbose mode: verify that asterisk is running
if [ "$verbose" != 'no' ] 
	then
	say "Checking channels configured in Asterisk:"
	sleep 1 # give it some time. This is enough on our simple test server
	if [ -x ast-cmd ]
	then
		ast-cmd cmd "zap show channels"
	else
		asterisk -rx "zap show channels"
	fi
fi

# vim:ts=2:
