FreeBSD-5.3/usr.sbin/i4b/isdnd/support.c

Compare this file to the similar file:
Show the results in this format:

/*
 * Copyright (c) 1997, 2003 Hellmuth Michaelis. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND 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 AUTHOR OR 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.
 *
 *---------------------------------------------------------------------------
 *
 *	i4b daemon - misc support routines
 *	----------------------------------
 *
 * $FreeBSD: src/usr.sbin/i4b/isdnd/support.c,v 1.16 2003/07/31 09:13:22 hm Exp $
 *
 *      last edit-date: [Thu Jul 31 11:05:16 2003]
 *
 *---------------------------------------------------------------------------*/

#include "isdnd.h"

static int isvalidtime(cfg_entry_t *cep);
	
/*---------------------------------------------------------------------------*
 *	find an active entry by driver type and driver unit
 *---------------------------------------------------------------------------*/
cfg_entry_t *
find_active_entry_by_driver(int drivertype, int driverunit)
{
	cfg_entry_t *cep = NULL;
	int i;

	for(i=0; i < nentries; i++)
	{
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		if(!((cep->usrdevicename == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_VALID, (log(LL_DBG, "find_active_entry_by_driver: entry %d, time not valid!", i)));
			continue;
		}
		
		/* found */
		
		if(cep->cdid == CDID_UNUSED)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_UNUSED !",
				i, bdrivername(drivertype), driverunit)));
			return(NULL);
		}
		else if(cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_active_entry_by_driver: entry %d [%s%d], cdid=CDID_RESERVED!",
				i, bdrivername(drivertype), driverunit)));
			return(NULL);
		}
		return(cep);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
cfg_entry_t *
find_by_device_for_dialout(int drivertype, int driverunit)
{
	cfg_entry_t *cep = NULL;
	int i;

	for(i=0; i < nentries; i++)
	{
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		/* compare driver type and unit */

		if(!((cep->usrdevicename == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, time not valid!", i)));
			continue;
		}
		
		/* found, check if already reserved */
		
		if(cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid reserved!", i)));
			return(NULL);
		}

		/* check if this entry is already in use ? */
		
		if(cep->cdid != CDID_UNUSED)	
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, cdid in use", i)));
			return(NULL);
		}

		if((setup_dialout(cep)) == GOOD)
		{
			/* found an entry to be used for calling out */
		
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: found entry %d!", i)));
			return(cep);
		}
		else
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: entry %d, setup_dialout() failed!", i)));
			return(NULL);
		}
	}

	DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialout: no entry found!")));
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
cfg_entry_t *
find_by_device_for_dialoutnumber(msg_dialoutnumber_ind_t *mp)
{
	cfg_entry_t *cep = NULL;
	int i, j;

	for(i=0; i < nentries; i++)
	{
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		/* compare driver type and unit */

		if(!((cep->usrdevicename == mp->driver) &&
		     (cep->usrdeviceunit == mp->driver_unit)))
		{
			continue;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, time not valid!", i)));
			continue;
		}

		/* found, check if already reserved */
		
		if(cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid reserved!", i)));
			return(NULL);
		}

		/* check if this entry is already in use ? */
		
		if(cep->cdid != CDID_UNUSED)	
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, cdid in use", i)));
			return(NULL);
		}

		cep->keypad[0] = '\0';
		
		/* check number and copy to cep->remote_numbers[] */
		
		for(j = 0; j < mp->cmdlen; j++)
		{
			if(!(isdigit(*(mp->cmd+j))))
			{
				DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, dial string contains non-digit at pos %d", i, j)));
				return(NULL);
			}
			/* fill in number to dial */
			cep->remote_numbers[0].number[j] = *(mp->cmd+j);
		}				
		cep->remote_numbers[0].number[j] = '\0';

/* XXX subaddr does not have to be a digit! isgraph() would be a better idea */
		for(j = 0; j < mp->subaddrlen; j++)
		{
			if(!(isdigit(*(mp->subaddr+j))))
			{
				DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, subaddr string contains non-digit at pos %d", i, j)));
				return(NULL);
			}
			/* fill in number to dial */
			cep->remote_numbers[0].subaddr[j] = *(mp->subaddr+j);
		}				
		cep->remote_numbers[0].subaddr[j] = '\0';

		cep->remote_numbers_count = 1;

		if((setup_dialout(cep)) == GOOD)
		{
			/* found an entry to be used for calling out */
		
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: found entry %d!", i)));
			return(cep);
		}
		else
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: entry %d, setup_dialout() failed!", i)));
			return(NULL);
		}
	}

	DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_dialoutnumber: no entry found!")));
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for send keypad
 *---------------------------------------------------------------------------*/
cfg_entry_t *
find_by_device_for_keypad(int drivertype, int driverunit, int cmdlen, char *cmd)
{
	cfg_entry_t *cep = NULL;
	int i, j;

	for(i=0; i < nentries; i++)
	{
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		/* compare driver type and unit */

		if(!((cep->usrdevicename == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, time not valid!", i)));
			continue;
		}

		/* found, check if already reserved */
		
		if(cep->cdid == CDID_RESERVED)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid reserved!", i)));
			return(NULL);
		}

		/* check if this entry is already in use ? */
		
		if(cep->cdid != CDID_UNUSED)	
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, cdid in use", i)));
			return(NULL);
		}

		cep->remote_numbers[0].number[0] = '\0';
		cep->remote_numbers_count = 0;
		cep->remote_phone_dialout.number[0] = '\0';
		
		bzero(cep->keypad, KEYPAD_MAX);
		strncpy(cep->keypad, cmd, cmdlen);

		DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, keypad string is %s", i, cep->keypad)));

		if((setup_dialout(cep)) == GOOD)
		{
			/* found an entry to be used for calling out */
		
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: found entry %d!", i)));
			return(cep);
		}
		else
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: entry %d, setup_dialout() failed!", i)));
			return(NULL);
		}
	}

	DBGL(DL_MSG, (log(LL_DBG, "find_by_device_for_keypad: no entry found!")));
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit and setup for dialing out
 *---------------------------------------------------------------------------*/
int
setup_dialout(cfg_entry_t *cep)
{
        int i;
    
	/* check controller operational */

	if((get_controller_state(cep->isdncontroller)) != CTRL_UP)
	{
		DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, controller is down", cep->name)));
		return(ERROR);
	}

	cep->isdncontrollerused = cep->isdncontroller;

	/* check channel available */

	switch(cep->isdnchannel)
	{
		case CHAN_ANY:
		    for (i = 0; i < isdn_ctrl_tab[cep->isdncontroller].nbch; i++)
			{
			if(ret_channel_state(cep->isdncontroller, i) == CHAN_IDLE)
			break;
		    }

		    if (i == isdn_ctrl_tab[cep->isdncontroller].nbch)
			{
				DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, no channel free", cep->name)));
				return(ERROR);
			}
			cep->isdnchannelused = CHAN_ANY;
			break;

		default:
			if((ret_channel_state(cep->isdncontroller, cep->isdnchannel)) != CHAN_IDLE)
			{
				DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s, channel not free", cep->name)));
			return(ERROR);
			}
			cep->isdnchannelused = cep->isdnchannel;
			break;
	}

	DBGL(DL_MSG, (log(LL_DBG, "setup_dialout: entry %s ok!", cep->name)));

	/* preset disconnect cause */
	
	SET_CAUSE_TYPE(cep->disc_cause, CAUSET_I4B);
	SET_CAUSE_VAL(cep->disc_cause, CAUSE_I4B_NORMAL);
	
	return(GOOD);
}

/*---------------------------------------------------------------------------*
 *	find entry by drivertype and driverunit
 *---------------------------------------------------------------------------*/
cfg_entry_t *
get_cep_by_driver(int drivertype, int driverunit)
{
	cfg_entry_t *cep = NULL;
	int i;

	for(i=0; i < nentries; i++)
	{
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		if(!((cep->usrdevicename == drivertype) &&
		     (cep->usrdeviceunit == driverunit)))
		{
			continue;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: entry %d, time not valid!", i)));
			continue;
		}		

		DBGL(DL_MSG, (log(LL_DBG, "get_cep_by_driver: found entry %d!", i)));
		return(cep);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	find a matching entry for an incoming call
 *
 *	- not found/no match: log output with LL_CHD and return NULL
 *	- found/match: make entry in free cep, return address
 *---------------------------------------------------------------------------*/
cfg_entry_t *
find_matching_entry_incoming(msg_connect_ind_t *mp)
{
	cfg_entry_t *cep = NULL;
	int i;

	/* check for CW (call waiting) early */

	if(mp->channel == CHAN_NO)
	{
		if(aliasing)
	        {
			char *src_tela = "ERROR-src_tela";
			char *dst_tela = "ERROR-dst_tela";
	
	                src_tela = get_alias(mp->src_telno);
	                dst_tela = get_alias(mp->dst_telno);
	
			log(LL_CHD, "%05d <unknown> CW from %s to %s (no channel free)",
				mp->header.cdid, src_tela, dst_tela);
		}
		else
		{
			log(LL_CHD, "%05d <unknown> call waiting from %s to %s (no channel free)",
				mp->header.cdid, mp->src_telno, mp->dst_telno);
		}
		return(NULL);
	}
	
	for(i=0; i < nentries; i++)
	{
		int n;
		cep = &cfg_entry_tab[i];	/* ptr to config entry */

		/* check my number */

		if(strncmp(cep->local_phone_incoming.number, mp->dst_telno, strlen(cep->local_phone_incoming.number)))
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, myno %s != incomingno %s", i,
				cep->local_phone_incoming.number, mp->dst_telno)));
			continue;
		}

		if (cep->usesubaddr && strncmp(cep->local_phone_incoming.subaddr, mp->dst_subaddr, strlen(cep->local_phone_incoming.subaddr)))
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, mysubno %s != incomingsubno %s", i,
				cep->local_phone_incoming.subaddr, mp->dst_subaddr)));
			continue;
		}

  		/* check all allowed remote numbers for this entry */

		for (n = 0; n < cep->incoming_numbers_count; n++)
		{
			incoming_number_t *in = &cep->remote_phone_incoming[n];

			/*
			 * An incoming number matches whenever the main 
			 * phone number either matches or is a wildcard AND
			 * subaddresses are either not in use or match as 
			 * well (or the required subaddress is a wildcard). 
			 * This means that if subaddresses are in use and
                         * the main phone number is a wildcard, the 
			 * subaddress is still required to match. 
			 *
			 * At first glance, this does not seem logical,
			 * but since subaddress usage can be configured per
			 * entry, disregarding the subaddress if the main 
			 * number matches would needlessly limit the user's
			 * flexibility.
			 */

			if ((in->number[0] == '*') || (!strncmp(in->number, mp->src_telno, strlen(in->number))))
			{
				if ((!cep->usesubaddr) || (in->subaddr[0] == '*') || (!strncmp(in->subaddr, mp->src_subaddr, strlen(in->subaddr))))
				{
					DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, match: remno %s = incomingfromno %s", i,
						in->number, mp->src_telno)));
					break;
				}
			}
			else
			{
				DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, remno %s != incomingfromno %s", i,
					in->number, mp->src_telno)));
			}
		}

		/* If all configured remote numbers have been tested without success, proceed to the next entry. */
		if (n >= cep->incoming_numbers_count)
			continue;

		/* check b protocol */

		if(cep->b1protocol != mp->bprot)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, bprot %d != incomingprot %d", i,
				cep->b1protocol, mp->bprot)));
			continue;
		}

		/* is this entry currently in use ? */

		if(cep->cdid != CDID_UNUSED)
		{
			if(cep->cdid == CDID_RESERVED)
			{
				DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid is reserved", i)));
			}
			else if (cep->dialin_reaction == REACT_ACCEPT
				 && cep->dialouttype == DIALOUT_CALLEDBACK)
			{
				/*
				 * We might consider doing this even if this is
				 * not a calledback config entry - BUT: there are
				 * severe race conditions and timinig problems
				 * ex. if both sides run I4B with no callback
				 * delay - both may shutdown the outgoing call
				 * and never be able to establish a connection.
				 * In the called-back case this should not happen.
				 */
				DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, incoming call for callback in progress (cdid %05d)", i, cep->cdid)));

				/* save the current call state, we're going to overwrite it with the
				 * new incoming state below... */
				cep->saved_call.cdid = cep->cdid;
				cep->saved_call.controller = cep->isdncontrollerused;
				cep->saved_call.channel = cep->isdnchannelused;
			}
			else
			{
				DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, cdid in use", i)));
				continue;	/* yes, next */
			}
		}

		/* check controller value ok */

		if(mp->controller > ncontroller)
		{
			log(LL_CHD, "%05d %s incoming call with invalid controller %d",
                        	mp->header.cdid, cep->name, mp->controller);
			return(NULL);
		}

		/* check controller marked up */

		if((get_controller_state(mp->controller)) != CTRL_UP)
		{
			log(LL_CHD, "%05d %s incoming call, controller %d DOWN!",
                        	mp->header.cdid, cep->name, mp->controller);
			return(NULL);
		}

		/* 
		 * check controller he wants, check for any 
		 * controller or specific controller 
		 */

		if( (mp->controller != -1) && 
		    (mp->controller != cep->isdncontroller) )
		{
			log(LL_CHD, "%05d %s incoming call, controller %d != incoming %d",
				mp->header.cdid, cep->name, 
				cep->isdncontroller, mp->controller);
			continue;
		}

		/* check channel he wants */

		switch(mp->channel)
		{
			case CHAN_ANY:
			    for (i = 0; i < isdn_ctrl_tab[mp->controller].nbch; i++)
				{
				if(ret_channel_state(mp->controller, i) == CHAN_IDLE)
				break;
			    }

			    if (i == isdn_ctrl_tab[mp->controller].nbch)
				{
					log(LL_CHD, "%05d %s incoming call, no channel free!",
	                	        	mp->header.cdid, cep->name);
					return(NULL);
				}
				break;

			case CHAN_NO:
				log(LL_CHD, "%05d %s incoming call, call waiting (no channel available)!",
                	        	mp->header.cdid, cep->name);
                	        return(NULL);
                	        break;

			default:
				if((ret_channel_state(mp->controller, mp->channel)) != CHAN_IDLE)
				{
					log(LL_CHD, "%05d %s incoming call, channel B%d not free!",
	                	        	mp->header.cdid, cep->name, mp->channel+1);
				return(NULL);
				}
				break;
		}

		/* check time interval */
		
		if(isvalidtime(cep) == 0)
		{
			DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, time not valid!", i)));
			continue;
		}
		
		/* found a matching entry */

		cep->cdid = mp->header.cdid;
		cep->isdncontrollerused = mp->controller;
		cep->isdnchannelused = mp->channel;
/*XXX*/		cep->disc_cause = 0;
		
		/* cp number to real one used */
		
		strcpy(cep->real_phone_incoming.number, mp->src_telno);

		/* copy display string */
		
		strcpy(cep->display, mp->display);
		
		/* entry currently down ? */
		
		if(cep->state == ST_DOWN)
		{
			msg_updown_ind_t mui;
			
			/* set interface up */
	
			DBGL(DL_MSG, (log(LL_DBG, "find_matching_entry_incoming: entry %d, ", i)));
	
			mui.driver = cep->usrdevicename;
			mui.driver_unit = cep->usrdeviceunit;
			mui.updown = SOFT_ENA;
			
			if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
			{
				log(LL_ERR, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
				error_exit(1, "find_matching_entry_incoming: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
			}

			cep->down_retry_count = 0;
			cep->state = ST_IDLE;
		}
		return(cep);
	}

	if(aliasing)
        {
		char *src_tela = "ERROR-src_tela";
		char *dst_tela = "ERROR-dst_tela";

                src_tela = get_alias(mp->src_telno);
                dst_tela = get_alias(mp->dst_telno);

		log(LL_CHD, "%05d Call from %s to %s",
			mp->header.cdid, src_tela, dst_tela);
	}
	else
	{
		log(LL_CHD, "%05d <unknown> incoming call from %s to %s ctrl %d",
			mp->header.cdid, mp->src_telno, mp->dst_telno, mp->controller);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	return address of ACTIVE config entry by controller and channel
 *---------------------------------------------------------------------------*/
cfg_entry_t *
get_cep_by_cc(int ctrlr, int chan)
{
	int i;

	if((chan < 0) || (chan >= isdn_ctrl_tab[ctrlr].nbch))
		return(NULL);
		
	for(i=0; i < nentries; i++)
	{
		if((cfg_entry_tab[i].cdid != CDID_UNUSED)		&&
		   (cfg_entry_tab[i].cdid != CDID_RESERVED)		&&
		   (cfg_entry_tab[i].isdnchannelused == chan)		&&
		   (cfg_entry_tab[i].isdncontrollerused == ctrlr)	&&
		   ((ret_channel_state(ctrlr, chan)) == CHAN_RUN))
		{
			return(&cfg_entry_tab[i]);
		}
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	return address of config entry identified by cdid
 *---------------------------------------------------------------------------*/
cfg_entry_t *
get_cep_by_cdid(int cdid)
{
	int i;

	for(i=0; i < nentries; i++)
	{
		if(cfg_entry_tab[i].cdid == cdid
		  || cfg_entry_tab[i].saved_call.cdid == cdid)
			return(&cfg_entry_tab[i]);
	}
	return(NULL);
}

/*---------------------------------------------------------------------------*
 *	return b channel driver type name string
 *---------------------------------------------------------------------------*/
char *
bdrivername(int drivertype)
{
	static char *bdtab[] = {
		"rbch",
		"tel",
		"ipr",
		"isp",
		"ibc",
		"ing"
	};

	if(drivertype >= BDRV_RBCH && drivertype <= BDRV_ING)
		return(bdtab[drivertype]);
	else
		return("unknown");
}

/*---------------------------------------------------------------------------*
 *	process AOCD charging messages
 *---------------------------------------------------------------------------*/
void
handle_charge(cfg_entry_t *cep)
{
	time_t now = time(NULL);

	if(cep->aoc_last == 0)		/* no last timestamp yet ? */
	{
		cep->aoc_last = now;	/* add time stamp */
	}
	else if(cep->aoc_now == 0)	/* no current timestamp yet ? */
	{
		cep->aoc_now = now;	/* current timestamp */
	}
	else
	{
		cep->aoc_last = cep->aoc_now;
		cep->aoc_now = now;
		cep->aoc_diff = cep->aoc_now - cep->aoc_last;
		cep->aoc_valid = AOC_VALID;
	}
	
#ifdef USE_CURSES
	if(do_fullscreen)
		display_charge(cep);
#endif

#ifdef I4B_EXTERNAL_MONITOR
	if(do_monitor && accepted)
		monitor_evnt_charge(cep, cep->charge, 0);
#endif

	if(cep->aoc_valid == AOC_VALID)
	{
		if(cep->aoc_diff != cep->unitlength)
		{
			DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length updated %d -> %d secs", cep->unitlength, cep->aoc_diff)));

			cep->unitlength = cep->aoc_diff;

			unitlen_chkupd(cep);
		}
		else
		{
#ifdef NOTDEF
			DBGL(DL_MSG, (log(LL_DBG, "handle_charge: AOCD unit length still %d secs", cep->unitlength)));
#endif
		}
	}
}

/*---------------------------------------------------------------------------*
 *	update kernel idle_time, earlyhup_time and unitlen_time
 *---------------------------------------------------------------------------*/
void
unitlen_chkupd(cfg_entry_t *cep)
{
	msg_timeout_upd_t tupd;

	tupd.cdid = cep->cdid;

	/* init the short hold data based on the shorthold algorithm type */
	
	switch(cep->shorthold_algorithm)
	{
		case SHA_FIXU:
			tupd.shorthold_data.shorthold_algorithm = SHA_FIXU;
			tupd.shorthold_data.unitlen_time = cep->unitlength;
			tupd.shorthold_data.idle_time = cep->idle_time_out;
			tupd.shorthold_data.earlyhup_time = cep->earlyhangup;
			break;

		case SHA_VARU:
			tupd.shorthold_data.shorthold_algorithm = SHA_VARU;
			tupd.shorthold_data.unitlen_time = cep->unitlength;
			tupd.shorthold_data.idle_time = cep->idle_time_out;
			tupd.shorthold_data.earlyhup_time = 0;
			break;
		default:
			log(LL_ERR, "unitlen_chkupd bad shorthold_algorithm %d", cep->shorthold_algorithm );
			return;
			break;			
	}

	if((ioctl(isdnfd, I4B_TIMEOUT_UPD, &tupd)) < 0)
	{
		log(LL_ERR, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
		error_exit(1, "ioctl I4B_TIMEOUT_UPD failed: %s", strerror(errno));
	}
}

/*--------------------------------------------------------------------------*
 *	this is intended to be called by do_exit and closes down all
 *	active connections before the daemon exits or is reconfigured.
 *--------------------------------------------------------------------------*/
void
close_allactive(void)
{
	int i, j, k;
	cfg_entry_t *cep = NULL;

	j = 0;
	
	for (i = 0; i < ncontroller; i++)
	{
		if((get_controller_state(i)) != CTRL_UP)
			continue;

		for (k = 0; k < isdn_ctrl_tab[i].nbch; k++)
		{
		    if((ret_channel_state(i, k)) == CHAN_RUN)
			{
			if((cep = get_cep_by_cc(i, k)) != NULL)
			{
#ifdef USE_CURSES
				if(do_fullscreen)
					display_disconnect(cep);
#endif
#ifdef I4B_EXTERNAL_MONITOR
				monitor_evnt_disconnect(cep);
#endif
				next_state(cep, EV_DRQ);
				j++;				
			}
		    }
		}
	}

	if(j)
	{
		log(LL_DMN, "close_allactive: waiting for all connections terminated");
		sleep(5);
	}
}

/*--------------------------------------------------------------------------*
 *	set an interface up
 *--------------------------------------------------------------------------*/
void
if_up(cfg_entry_t *cep)
{
	msg_updown_ind_t mui;
			
	/* set interface up */
	
	DBGL(DL_MSG, (log(LL_DBG, "if_up: taking %s%d up", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
	
	mui.driver = cep->usrdevicename;
	mui.driver_unit = cep->usrdeviceunit;
	mui.updown = SOFT_ENA;
			
	if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
	{
		log(LL_ERR, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
		error_exit(1, "if_up: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
	}
	cep->down_retry_count = 0;

#ifdef USE_CURSES
	if(do_fullscreen)
		display_updown(cep, 1);
#endif
#ifdef I4B_EXTERNAL_MONITOR
	monitor_evnt_updown(cep, 1);
#endif
	
}

/*--------------------------------------------------------------------------*
 *	set an interface down
 *--------------------------------------------------------------------------*/
void
if_down(cfg_entry_t *cep)
{
	msg_updown_ind_t mui;
			
	/* set interface up */
	
	DBGL(DL_MSG, (log(LL_DBG, "if_down: taking %s%d down", bdrivername(cep->usrdevicename), cep->usrdeviceunit)));
	
	mui.driver = cep->usrdevicename;
	mui.driver_unit = cep->usrdeviceunit;
	mui.updown = SOFT_DIS;
			
	if((ioctl(isdnfd, I4B_UPDOWN_IND, &mui)) < 0)
	{
		log(LL_ERR, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
		error_exit(1, "if_down: ioctl I4B_UPDOWN_IND failed: %s", strerror(errno));
	}
	cep->went_down_time = time(NULL);
	cep->down_retry_count = 0;

#ifdef USE_CURSES
	if(do_fullscreen)
		display_updown(cep, 0);
#endif
#ifdef I4B_EXTERNAL_MONITOR
	monitor_evnt_updown(cep, 0);
#endif

}

/*--------------------------------------------------------------------------*
 *	send a dial response to (an interface in) the kernel 
 *--------------------------------------------------------------------------*/
void
dialresponse(cfg_entry_t *cep, int dstat)
{
	msg_dialout_resp_t mdr;

	static char *stattab[] = {
		"normal condition",
		"temporary failure",
		"permanent failure",
		"dialout not allowed"
	};

	if(dstat < DSTAT_NONE || dstat > DSTAT_INONLY)
	{
		log(LL_ERR, "dialresponse: dstat out of range %d!", dstat);
		return;
	}
	
	mdr.driver = cep->usrdevicename;
	mdr.driver_unit = cep->usrdeviceunit;
	mdr.stat = dstat;
	mdr.cause = cep->disc_cause;	
	
	if((ioctl(isdnfd, I4B_DIALOUT_RESP, &mdr)) < 0)
	{
		log(LL_ERR, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
		error_exit(1, "dialresponse: ioctl I4B_DIALOUT_RESP failed: %s", strerror(errno));
	}

	DBGL(DL_DRVR, (log(LL_DBG, "dialresponse: sent [%s]", stattab[dstat])));
}

/*--------------------------------------------------------------------------*
 *	screening/presentation indicator
 *--------------------------------------------------------------------------*/
void
handle_scrprs(int cdid, int scr, int prs, char *caller)
{
	/* screening indicator */
	
	if(scr < SCR_NONE || scr > SCR_NET)
	{
		log(LL_ERR, "msg_connect_ind: invalid screening indicator value %d!", scr);
	}
	else
	{
		static char *scrtab[] = {
			"no screening indicator",
			"sreening user provided, not screened",
			"screening user provided, verified & passed",
			"screening user provided, verified & failed",
			"screening network provided", };

		if(extcallattr)
		{
			log(LL_CHD, "%05d %s %s", cdid, caller, scrtab[scr]);
		}
		else
		{
			DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, scrtab[scr])));
		}
	}
			
	/* presentation indicator */
	
	if(prs < PRS_NONE || prs > PRS_RESERVED)
	{
		log(LL_ERR, "msg_connect_ind: invalid presentation indicator value %d!", prs);
	}
	else
	{
		static char *prstab[] = {
			"no presentation indicator",
			"presentation allowed",
			"presentation restricted",
			"number not available due to interworking",
			"reserved presentation value" };
			
		if(extcallattr)
		{
			log(LL_CHD, "%05d %s %s", cdid, caller, prstab[prs]);
		}
		else
		{
			DBGL(DL_MSG, (log(LL_DBG, "%s - %s", caller, prstab[prs])));
		}
	}
}

/*--------------------------------------------------------------------------*
 *	check if the time is valid for an entry
 *--------------------------------------------------------------------------*/
static int 
isvalidtime(cfg_entry_t *cep)
{
	time_t t;
	struct tm *tp;

	if(cep->day == 0)
		return(1);

	t = time(NULL);
	tp = localtime(&t);

	if(cep->day & HD)
	{
		if(isholiday(tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900))
		{
			DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: holiday %d.%d.%d", tp->tm_mday, (tp->tm_mon)+1, (tp->tm_year)+1900)));
			goto dayok;
		}
	}
	
	if(cep->day & (1 << tp->tm_wday))
	{
		DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: day match")));	
		goto dayok;
	}

	return(0);
	
dayok:
	if(cep->fromhr==0 && cep->frommin==0 && cep->tohr==0 && cep->tomin==0)
	{
		DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: no time specified, match!")));
		return(1);
	}

	if(cep->tohr < cep->fromhr)
	{
		/* before 00:00 */
		
		if( (tp->tm_hour > cep->fromhr) ||
		    (tp->tm_hour == cep->fromhr && tp->tm_min > cep->frommin) )
		{
			DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-1, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}

		/* after 00:00 */
		
		if( (tp->tm_hour < cep->tohr) ||
		    (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
		{
			DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t<f-2, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}
	}
	else if(cep->fromhr == cep->tohr)
	{
		if(tp->tm_min >= cep->frommin && tp->tm_min < cep->tomin)
		{
			DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: f=t, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			
			return(1);
		}
	}
	else
	{
		if((tp->tm_hour > cep->fromhr && tp->tm_hour < cep->tohr) ||
		   (tp->tm_hour == cep->fromhr && tp->tm_min >= cep->frommin) ||
		   (tp->tm_hour == cep->tohr && tp->tm_min < cep->tomin) )
		{
			DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: t>f, spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, match!",
				cep->fromhr, cep->frommin,
				cep->tohr, cep->tomin,
				tp->tm_hour, tp->tm_min)));
			return(1);
		}
	}
	DBGL(DL_VALID, (log(LL_DBG, "isvalidtime: spec=%02d:%02d-%02d:%02d, curr=%02d:%02d, no match!",
			cep->fromhr, cep->frommin,
			cep->tohr, cep->tomin,
			tp->tm_hour, tp->tm_min)));

	return(0);	
}

/*--------------------------------------------------------------------------*
 *	prepend national or international prefix to a number
 *--------------------------------------------------------------------------*/
int add_number_prefix(char *number, int type_of_number)
{
	char tmp[TELNO_MAX];
	char *prefix;
	int result = 0;

	if (type_of_number == TON_NATIONAL || type_of_number == TON_INTERNAT)
	{
		if (type_of_number == TON_NATIONAL)
			prefix = prefixnational;
		else 
			prefix = prefixinternational;
		
		/* Add prefix only if not already there */
		if (strncmp(number, prefix, strlen(prefix)) != 0)
		{
			snprintf(tmp, sizeof(tmp)-1, "%s%s", prefix, number);
			strncpy(number, tmp, TELNO_MAX-1);
			result = 1;
		}
	}

	return result;
}

/* EOF */