Skip to content
Snippets Groups Projects
sig_pri.c 97 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 1999 - 2009, Digium, Inc.
     *
     * Mark Spencer <markster@digium.com>
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    /*! \file
    
     * \brief PRI signaling module
     *
     * \author Matthew Fredrickson <creslin@digium.com>
     */
    
    
    #include "asterisk.h"
    
    #ifdef HAVE_PRI
    
    #include <errno.h>
    #include <ctype.h>
    #include <signal.h>
    
    #include "asterisk/utils.h"
    #include "asterisk/options.h"
    #include "asterisk/pbx.h"
    #include "asterisk/file.h"
    #include "asterisk/callerid.h"
    #include "asterisk/say.h"
    #include "asterisk/manager.h"
    #include "asterisk/astdb.h"
    #include "asterisk/causes.h"
    #include "asterisk/musiconhold.h"
    #include "asterisk/cli.h"
    #include "asterisk/transcap.h"
    #include "asterisk/features.h"
    
    #include "sig_pri.h"
    
    
    /* define this to send PRI user-user information elements */
    #undef SUPPORT_USERUSER
    
    
    #if 0
    #define DEFAULT_PRI_DEBUG (PRI_DEBUG_Q931_DUMP | PRI_DEBUG_Q921_DUMP | PRI_DEBUG_Q921_RAW | PRI_DEBUG_Q921_STATE)
    #else
    #define DEFAULT_PRI_DEBUG 0
    #endif
    
    
    static int pri_matchdigittimeout = 3000;
    
    static int pri_gendigittimeout = 8000;
    
    #define DCHAN_NOTINALARM  (1 << 0)
    #define DCHAN_UP          (1 << 1)
    
    #define PRI_CHANNEL(p) ((p) & 0xff)
    #define PRI_SPAN(p) (((p) >> 8) & 0xff)
    #define PRI_EXPLICIT(p) (((p) >> 16) & 0x01)
    
    
    #define DCHAN_AVAILABLE	(DCHAN_NOTINALARM | DCHAN_UP)
    
    
    #define PRI_DEADLOCK_AVOIDANCE(p) \
    
    	do { \
    		sig_pri_unlock_private(p); \
    		usleep(1); \
    		sig_pri_lock_private(p); \
    	} while (0)
    
    static int pri_active_dchan_index(struct sig_pri_pri *pri);
    
    static inline void pri_rel(struct sig_pri_pri *pri)
    {
    	ast_mutex_unlock(&pri->lock);
    }
    
    static unsigned int PVT_TO_CHANNEL(struct sig_pri_chan *p)
    {
    
    	int res = (((p)->prioffset) | ((p)->logicalspan << 8) | (p->mastertrunkgroup ? 0x10000 : 0));
    	ast_debug(5, "prioffset: %d mastertrunkgroup: %d logicalspan: %d result: %d\n",
    		p->prioffset, p->mastertrunkgroup, p->logicalspan, res);
    
    }
    
    static void sig_pri_handle_dchan_exception(struct sig_pri_pri *pri, int index)
    {
    	if (pri->calls->handle_dchan_exception)
    		pri->calls->handle_dchan_exception(pri, index);
    }
    
    
    static void sig_pri_set_dialing(struct sig_pri_chan *p, int flag)
    {
    	if (p->calls->set_dialing)
    
    		p->calls->set_dialing(p->chan_pvt, flag);
    }
    
    /*!
     * \internal
     * \brief Set the caller id information in the parent module.
     * \since 1.6.3
     *
     * \param p sig_pri channel structure.
     *
     * \return Nothing
     */
    static void sig_pri_set_caller_id(struct sig_pri_chan *p)
    {
    	struct ast_party_caller caller;
    
    	if (p->calls->set_callerid) {
    		ast_party_caller_init(&caller);
    		caller.id.number = p->cid_num;
    		caller.id.name = p->cid_name;
    		caller.id.number_type = p->cid_ton;
    		caller.id.number_presentation = p->callingpres;
    		caller.ani = p->cid_ani;
    		caller.ani2 = p->cid_ani2;
    		p->calls->set_callerid(p->chan_pvt, &caller);
    	}
    }
    
    /*!
     * \internal
     * \brief Set the Dialed Number Identifier.
     * \since 1.6.3
     *
     * \param p sig_pri channel structure.
     * \param dnid Dialed Number Identifier string.
     *
     * \return Nothing
     */
    static void sig_pri_set_dnid(struct sig_pri_chan *p, const char *dnid)
    {
    	if (p->calls->set_dnid) {
    		p->calls->set_dnid(p->chan_pvt, dnid);
    	}
    }
    
    /*!
     * \internal
     * \brief Set the Redirecting Directory Number Information Service (RDNIS).
     * \since 1.6.3
     *
     * \param p sig_pri channel structure.
     * \param rdnis Redirecting Directory Number Information Service (RDNIS) string.
     *
     * \return Nothing
     */
    static void sig_pri_set_rdnis(struct sig_pri_chan *p, const char *rdnis)
    {
    	if (p->calls->set_rdnis) {
    		p->calls->set_rdnis(p->chan_pvt, rdnis);
    	}
    
    static void sig_pri_unlock_private(struct sig_pri_chan *p)
    {
    	if (p->calls->unlock_private)
    		p->calls->unlock_private(p->chan_pvt);
    }
    
    static void sig_pri_lock_private(struct sig_pri_chan *p)
    {
    	if (p->calls->lock_private)
    		p->calls->lock_private(p->chan_pvt);
    }
    
    static inline int pri_grab(struct sig_pri_chan *p, struct sig_pri_pri *pri)
    {
    	int res;
    	/* Grab the lock first */
    	do {
    		res = ast_mutex_trylock(&pri->lock);
    		if (res) {
    			PRI_DEADLOCK_AVOIDANCE(p);
    		}
    	} while (res);
    	/* Then break the poll */
    	pthread_kill(pri->master, SIGURG);
    	return 0;
    }
    
    
    /*!
     * \internal
     * \brief Convert PRI redirecting reason to asterisk version.
     * \since 1.6.3
     *
     * \param pri_reason PRI redirecting reason.
     *
     * \return Equivalent asterisk redirecting reason value.
     */
    static enum AST_REDIRECTING_REASON pri_to_ast_reason(int pri_reason)
    {
    	enum AST_REDIRECTING_REASON ast_reason;
    
    	switch (pri_reason) {
    	case PRI_REDIR_FORWARD_ON_BUSY:
    		ast_reason = AST_REDIRECTING_REASON_USER_BUSY;
    		break;
    	case PRI_REDIR_FORWARD_ON_NO_REPLY:
    		ast_reason = AST_REDIRECTING_REASON_NO_ANSWER;
    		break;
    	case PRI_REDIR_DEFLECTION:
    		ast_reason = AST_REDIRECTING_REASON_DEFLECTION;
    		break;
    	case PRI_REDIR_UNCONDITIONAL:
    		ast_reason = AST_REDIRECTING_REASON_UNCONDITIONAL;
    		break;
    	case PRI_REDIR_UNKNOWN:
    	default:
    		ast_reason = AST_REDIRECTING_REASON_UNKNOWN;
    		break;
    	}
    
    	return ast_reason;
    }
    
    /*!
     * \internal
     * \brief Convert asterisk redirecting reason to PRI version.
     * \since 1.6.3
     *
     * \param ast_reason Asterisk redirecting reason.
     *
     * \return Equivalent PRI redirecting reason value.
     */
    static int ast_to_pri_reason(enum AST_REDIRECTING_REASON ast_reason)
    {
    	int pri_reason;
    
    	switch (ast_reason) {
    	case AST_REDIRECTING_REASON_USER_BUSY:
    		pri_reason = PRI_REDIR_FORWARD_ON_BUSY;
    		break;
    	case AST_REDIRECTING_REASON_NO_ANSWER:
    		pri_reason = PRI_REDIR_FORWARD_ON_NO_REPLY;
    		break;
    	case AST_REDIRECTING_REASON_UNCONDITIONAL:
    		pri_reason = PRI_REDIR_UNCONDITIONAL;
    		break;
    	case AST_REDIRECTING_REASON_DEFLECTION:
    		pri_reason = PRI_REDIR_DEFLECTION;
    		break;
    	case AST_REDIRECTING_REASON_UNKNOWN:
    	default:
    		pri_reason = PRI_REDIR_UNKNOWN;
    		break;
    	}
    
    	return pri_reason;
    }
    
    /*!
     * \internal
     * \brief Convert PRI number presentation to asterisk version.
     * \since 1.6.3
     *
     * \param pri_presentation PRI number presentation.
     *
     * \return Equivalent asterisk number presentation value.
     */
    static int pri_to_ast_presentation(int pri_presentation)
    {
    	int ast_presentation;
    
    	switch (pri_presentation) {
    	case PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
    		ast_presentation = AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
    		break;
    	case PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
    		ast_presentation = AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
    		break;
    	case PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
    		ast_presentation = AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN;
    		break;
    	case PRES_ALLOWED_NETWORK_NUMBER:
    		ast_presentation = AST_PRES_ALLOWED_NETWORK_NUMBER;
    		break;
    	case PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
    		ast_presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
    		break;
    	case PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
    		ast_presentation = AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN;
    		break;
    	case PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
    		ast_presentation = AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN;
    		break;
    	case PRES_PROHIB_NETWORK_NUMBER:
    		ast_presentation = AST_PRES_PROHIB_NETWORK_NUMBER;
    		break;
    	case PRES_NUMBER_NOT_AVAILABLE:
    		ast_presentation = AST_PRES_NUMBER_NOT_AVAILABLE;
    		break;
    	default:
    		ast_presentation = AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
    		break;
    	}
    
    	return ast_presentation;
    }
    
    /*!
     * \internal
     * \brief Convert asterisk number presentation to PRI version.
     * \since 1.6.3
     *
     * \param ast_presentation Asterisk number presentation.
     *
     * \return Equivalent PRI number presentation value.
     */
    static int ast_to_pri_presentation(int ast_presentation)
    {
    	int pri_presentation;
    
    	switch (ast_presentation) {
    	case AST_PRES_ALLOWED_USER_NUMBER_NOT_SCREENED:
    		pri_presentation = PRES_ALLOWED_USER_NUMBER_NOT_SCREENED;
    		break;
    	case AST_PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN:
    		pri_presentation = PRES_ALLOWED_USER_NUMBER_PASSED_SCREEN;
    		break;
    	case AST_PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN:
    		pri_presentation = PRES_ALLOWED_USER_NUMBER_FAILED_SCREEN;
    		break;
    	case AST_PRES_ALLOWED_NETWORK_NUMBER:
    		pri_presentation = PRES_ALLOWED_NETWORK_NUMBER;
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_NOT_SCREENED:
    		pri_presentation = PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_PASSED_SCREEN:
    		pri_presentation = PRES_PROHIB_USER_NUMBER_PASSED_SCREEN;
    		break;
    	case AST_PRES_PROHIB_USER_NUMBER_FAILED_SCREEN:
    		pri_presentation = PRES_PROHIB_USER_NUMBER_FAILED_SCREEN;
    		break;
    	case AST_PRES_PROHIB_NETWORK_NUMBER:
    		pri_presentation = PRES_PROHIB_NETWORK_NUMBER;
    		break;
    	case AST_PRES_NUMBER_NOT_AVAILABLE:
    		pri_presentation = PRES_NUMBER_NOT_AVAILABLE;
    		break;
    	default:
    		pri_presentation = PRES_PROHIB_USER_NUMBER_NOT_SCREENED;
    		break;
    	}
    
    	return pri_presentation;
    }
    
    /*!
     * \internal
     * \brief Determine the overall presentation value for the given party.
     * \since 1.6.3
     *
     * \param id Party to determine the overall presentation value.
     *
     * \return Overall presentation value for the given party converted to ast values.
     */
    static int overall_ast_presentation(const struct pri_party_id *id)
    {
    	int number_priority;
    	int number_value;
    	int number_screening;
    	int name_priority;
    	int name_value;
    
    	/* Determine name presentation priority. */
    	if (!id->name.valid) {
    		name_value = PRI_PRES_UNAVAILABLE;
    		name_priority = 3;
    	} else {
    		name_value = id->name.presentation & PRI_PRES_RESTRICTION;
    		switch (name_value) {
    		case PRI_PRES_RESTRICTED:
    			name_priority = 0;
    			break;
    		case PRI_PRES_ALLOWED:
    			name_priority = 1;
    			break;
    		case PRI_PRES_UNAVAILABLE:
    			name_priority = 2;
    			break;
    		default:
    			name_value = PRI_PRES_UNAVAILABLE;
    			name_priority = 3;
    			break;
    		}
    	}
    
    	/* Determine number presentation priority. */
    	if (!id->number.valid) {
    		number_screening = PRI_PRES_USER_NUMBER_UNSCREENED;
    		number_value = PRI_PRES_UNAVAILABLE;
    		number_priority = 3;
    	} else {
    		number_screening = id->number.presentation & PRI_PRES_NUMBER_TYPE;
    		number_value = id->number.presentation & PRI_PRES_RESTRICTION;
    		switch (number_value) {
    		case PRI_PRES_RESTRICTED:
    			number_priority = 0;
    			break;
    		case PRI_PRES_ALLOWED:
    			number_priority = 1;
    			break;
    		case PRI_PRES_UNAVAILABLE:
    			number_priority = 2;
    			break;
    		default:
    			number_screening = PRI_PRES_USER_NUMBER_UNSCREENED;
    			number_value = PRI_PRES_UNAVAILABLE;
    			number_priority = 3;
    			break;
    		}
    	}
    
    	/* Select the wining presentation value. */
    	if (name_priority < number_priority) {
    		number_value = name_value;
    	}
    
    	return pri_to_ast_presentation(number_value | number_screening);
    }
    
    /*!
     * \internal
     * \brief Fill in the PRI party id from the given asterisk party id.
     * \since 1.6.3
     *
     * \param pri_id PRI party id structure.
     * \param ast_id Asterisk party id structure.
     *
     * \return Nothing
     *
     * \note Assumes that pri_id has been previously memset to zero.
     */
    static void sig_pri_party_id_from_ast(struct pri_party_id *pri_id, const struct ast_party_id *ast_id)
    {
    	int presentation;
    
    	presentation = ast_to_pri_presentation(ast_id->number_presentation);
    	if (!ast_strlen_zero(ast_id->name)) {
    		pri_id->name.valid = 1;
    		pri_id->name.presentation = presentation;
    		pri_id->name.char_set = PRI_CHAR_SET_ISO8859_1;
    		ast_copy_string(pri_id->name.str, ast_id->name, sizeof(pri_id->name.str));
    	}
    	if (!ast_strlen_zero(ast_id->number)) {
    		pri_id->number.valid = 1;
    		pri_id->number.presentation = presentation;
    		pri_id->number.plan = ast_id->number_type;
    		ast_copy_string(pri_id->number.str, ast_id->number, sizeof(pri_id->number.str));
    	}
    }
    
    /*!
     * \internal
     * \brief Update the PRI redirecting information for the current call.
     * \since 1.6.3
     *
     * \param pvt sig_pri private channel structure.
     * \param ast Asterisk channel
     *
     * \return Nothing
     *
     * \note Assumes that the PRI lock is already obtained.
     */
    static void sig_pri_redirecting_update(struct sig_pri_chan *pvt, struct ast_channel *ast)
    {
    	struct pri_party_redirecting pri_redirecting;
    	struct ast_party_redirecting ast_redirecting;
    
    	/* Gather asterisk redirecting data */
    	ast_redirecting = ast->redirecting;
    	ast_redirecting.from.number = ast->cid.cid_rdnis;
    
    /*! \todo XXX Original called data can be put in a channel data store that is inherited. */
    
    	memset(&pri_redirecting, 0, sizeof(pri_redirecting));
    	sig_pri_party_id_from_ast(&pri_redirecting.from, &ast_redirecting.from);
    	sig_pri_party_id_from_ast(&pri_redirecting.to, &ast_redirecting.to);
    	pri_redirecting.count = ast_redirecting.count;
    	pri_redirecting.reason = ast_to_pri_reason(ast_redirecting.reason);
    
    	pri_redirecting_update(pvt->pri->pri, pvt->call, &pri_redirecting);
    }
    
    
    /*!
     * \internal
     * \brief Reset DTMF detector.
     * \since 1.6.3
     *
     * \param p sig_pri channel structure.
     *
     * \return Nothing
     */
    static void sig_pri_dsp_reset_and_flush_digits(struct sig_pri_chan *p)
    {
    	if (p->calls->dsp_reset_and_flush_digits) {
    		p->calls->dsp_reset_and_flush_digits(p->chan_pvt);
    	}
    }
    
    
    static int sig_pri_set_echocanceller(struct sig_pri_chan *p, int enable)
    {
    	if (p->calls->set_echocanceller)
    		return p->calls->set_echocanceller(p->chan_pvt, enable);
    	else
    		return -1;
    }
    
    static void sig_pri_fixup_chans(struct sig_pri_chan *old, struct sig_pri_chan *new)
    {
    	if (old->calls->fixup_chans)
    		old->calls->fixup_chans(old->chan_pvt, new->chan_pvt);
    }
    
    static int sig_pri_play_tone(struct sig_pri_chan *p, enum sig_pri_tone tone)
    {
    	if (p->calls->play_tone)
    		return p->calls->play_tone(p->chan_pvt, tone);
    	else
    		return -1;
    }
    
    
    static struct ast_channel *sig_pri_new_ast_channel(struct sig_pri_chan *p, int state, int startpbx, int ulaw, int transfercapability, char *exten, const struct ast_channel *requestor)
    
    {
    	struct ast_channel *c;
    
    	if (p->calls->new_ast_channel)
    
    		c = p->calls->new_ast_channel(p->chan_pvt, state, startpbx, ulaw, transfercapability, exten, requestor);
    
    	else
    		return NULL;
    
    	if (!p->owner)
    		p->owner = c;
    	p->isidlecall = 0;
    	p->alreadyhungup = 0;
    
    	return c;
    }
    
    
    struct ast_channel *sig_pri_request(struct sig_pri_chan *p, enum sig_pri_law law, const struct ast_channel *requestor)
    
    {
    	ast_log(LOG_DEBUG, "%s %d\n", __FUNCTION__, p->channel);
    
    
    	return sig_pri_new_ast_channel(p, AST_STATE_RESERVED, 0, law, 0, p->exten, requestor);
    
    }
    
    int pri_is_up(struct sig_pri_pri *pri)
    {
    	int x;
    	for (x = 0; x < NUM_DCHANS; x++) {
    		if (pri->dchanavail[x] == DCHAN_AVAILABLE)
    			return 1;
    	}
    	return 0;
    }
    
    static char *pri_order(int level)
    {
    	switch (level) {
    	case 0:
    		return "Primary";
    	case 1:
    		return "Secondary";
    	case 2:
    		return "Tertiary";
    	case 3:
    		return "Quaternary";
    	default:
    		return "<Unknown>";
    
    }
    
    /* Returns index of the active dchan */
    static int pri_active_dchan_index(struct sig_pri_pri *pri)
    {
    
    
    	for (x = 0; x < NUM_DCHANS; x++) {
    		if ((pri->dchans[x] == pri->pri))
    
    	ast_log(LOG_WARNING, "No active dchan found!\n");
    	return -1;
    
    }
    
    static int pri_find_dchan(struct sig_pri_pri *pri)
    {
    	int oldslot = -1;
    	struct pri *old;
    	int newslot = -1;
    	int x;
    	old = pri->pri;
    	for (x = 0; x < NUM_DCHANS; x++) {
    		if ((pri->dchanavail[x] == DCHAN_AVAILABLE) && (newslot < 0))
    			newslot = x;
    		if (pri->dchans[x] == old) {
    			oldslot = x;
    		}
    	}
    	if (newslot < 0) {
    		newslot = 0;
    		/* This is annoying to see on non persistent layer 2 connections.  Let's not complain in that case */
    		if (pri->sig != SIG_BRI_PTMP) {
    			ast_log(LOG_WARNING, "No D-channels available!  Using Primary channel as D-channel anyway!\n");
    		}
    	}
    	if (old && (oldslot != newslot))
    		ast_log(LOG_NOTICE, "Switching from d-channel fd %d to fd %d!\n",
    			pri->fds[oldslot], pri->fds[newslot]);
    	pri->pri = pri->dchans[newslot];
    	return 0;
    }
    
    static void pri_queue_frame(struct sig_pri_chan *p, struct ast_frame *f, struct sig_pri_pri *pri)
    {
    	/* We must unlock the PRI to avoid the possibility of a deadlock */
    	if (pri)
    		ast_mutex_unlock(&pri->lock);
    	for (;;) {
    		if (p->owner) {
    			if (ast_channel_trylock(p->owner)) {
    				PRI_DEADLOCK_AVOIDANCE(p);
    			} else {
    				ast_queue_frame(p->owner, f);
    				ast_channel_unlock(p->owner);
    				break;
    			}
    		} else
    			break;
    	}
    	if (pri)
    		ast_mutex_lock(&pri->lock);
    }
    
    static void pri_queue_control(struct sig_pri_chan *p, int subclass, struct sig_pri_pri *pri)
    {
    	struct ast_frame f = {AST_FRAME_CONTROL, };
    
    	f.subclass = subclass;
    	pri_queue_frame(p, &f, pri);
    }
    
    static int pri_find_principle(struct sig_pri_pri *pri, int channel)
    {
    	int x;
    	int span = PRI_SPAN(channel);
    	int principle = -1;
    	int explicit = PRI_EXPLICIT(channel);
    	channel = PRI_CHANNEL(channel);
    
    	if (!explicit) {
    		int index = pri_active_dchan_index(pri);
    		if (index == -1)
    			return -1;
    		span = pri->dchan_logical_span[index];
    	}
    
    	for (x = 0; x < pri->numchans; x++) {
    		if (pri->pvts[x] && (pri->pvts[x]->prioffset == channel) && (pri->pvts[x]->logicalspan == span)) {
    			principle = x;
    			break;
    		}
    	}
    
    	return principle;
    }
    
    static int pri_fixup_principle(struct sig_pri_pri *pri, int principle, q931_call *c)
    {
    	int x;
    	if (!c) {
    		if (principle < 0)
    			return -1;
    		return principle;
    	}
    
    	if ((principle > -1) &&
    		(principle < pri->numchans) &&
    		(pri->pvts[principle]) &&
    
    		(pri->pvts[principle]->call == c))
    		return principle;
    	/* First, check for other bearers */
    	for (x = 0; x < pri->numchans; x++) {
    		if (!pri->pvts[x])
    			continue;
    		if (pri->pvts[x]->call == c) {
    			/* Found our call */
    			if (principle != x) {
    				struct sig_pri_chan *new = pri->pvts[principle], *old = pri->pvts[x];
    
    				ast_verb(3, "Moving call from channel %d to channel %d\n",
    
    					old->channel, new->channel);
    
    				if (new->owner) {
    					ast_log(LOG_WARNING, "Can't fix up channel from %d to %d because %d is already in use\n",
    						old->channel, new->channel, new->channel);
    					return -1;
    				}
    
    				sig_pri_fixup_chans(old, new);
    				/* Fix it all up now */
    				new->owner = old->owner;
    				old->owner = NULL;
    
    				new->call = old->call;
    				old->call = NULL;
    
    			}
    			return principle;
    		}
    	}
    	ast_log(LOG_WARNING, "Call specified, but not found?\n");
    	return -1;
    }
    
    static char * redirectingreason2str(int redirectingreason)
    {
    	switch (redirectingreason) {
    	case 0:
    		return "UNKNOWN";
    	case 1:
    		return "BUSY";
    	case 2:
    		return "NO_REPLY";
    	case 0xF:
    		return "UNCONDITIONAL";
    	default:
    		return "NOREDIRECT";
    	}
    }
    
    static char *dialplan2str(int dialplan)
    {
    	if (dialplan == -1) {
    		return("Dynamically set dialplan in ISDN");
    	}
    	return (pri_plan2str(dialplan));
    }
    
    static void apply_plan_to_number(char *buf, size_t size, const struct sig_pri_pri *pri, const char *number, const int plan)
    {
    	switch (plan) {
    	case PRI_INTERNATIONAL_ISDN:		/* Q.931 dialplan == 0x11 international dialplan => prepend international prefix digits */
    		snprintf(buf, size, "%s%s", pri->internationalprefix, number);
    		break;
    	case PRI_NATIONAL_ISDN:			/* Q.931 dialplan == 0x21 national dialplan => prepend national prefix digits */
    		snprintf(buf, size, "%s%s", pri->nationalprefix, number);
    		break;
    	case PRI_LOCAL_ISDN:			/* Q.931 dialplan == 0x41 local dialplan => prepend local prefix digits */
    		snprintf(buf, size, "%s%s", pri->localprefix, number);
    		break;
    	case PRI_PRIVATE:			/* Q.931 dialplan == 0x49 private dialplan => prepend private prefix digits */
    		snprintf(buf, size, "%s%s", pri->privateprefix, number);
    		break;
    	case PRI_UNKNOWN:			/* Q.931 dialplan == 0x00 unknown dialplan => prepend unknown prefix digits */
    		snprintf(buf, size, "%s%s", pri->unknownprefix, number);
    		break;
    	default:				/* other Q.931 dialplan => don't twiddle with callingnum */
    		snprintf(buf, size, "%s", number);
    		break;
    	}
    }
    
    static int pri_check_restart(struct sig_pri_pri *pri)
    {
    #ifdef HAVE_PRI_SERVICE_MESSAGES
    tryanotherpos:
    #endif
    	do {
    		pri->resetpos++;
    	} while ((pri->resetpos < pri->numchans) &&
    		(!pri->pvts[pri->resetpos] ||
    		pri->pvts[pri->resetpos]->call ||
    		pri->pvts[pri->resetpos]->resetting));
    	if (pri->resetpos < pri->numchans) {
    #ifdef HAVE_PRI_SERVICE_MESSAGES
    		char db_chan_name[20], db_answer[5], state;
    		int why;
    
    		/* check if the channel is out of service */
    		ast_mutex_lock(&pri->pvts[pri->resetpos]->service_lock);
    		snprintf(db_chan_name, sizeof(db_chan_name), "%s/%d:%d", dahdi_db, pri->pvts[pri->resetpos]->pri->span, pri->pvts[pri->resetpos]->channel);
    		ast_mutex_unlock(&pri->pvts[pri->resetpos]->service_lock);
    
    		/* if so, try next channel */
    		if (!ast_db_get(db_chan_name, SRVST_DBKEY, db_answer, sizeof(db_answer))) {
    
    Tilghman Lesher's avatar
    Tilghman Lesher committed
    			sscanf(db_answer, "%1c:%30d", &state, &why);
    
    			if (why) {
    				ast_log(LOG_NOTICE, "span '%d' channel '%d' out-of-service (reason: %s), not sending RESTART\n", pri->span,
    				pri->pvts[pri->resetpos]->channel, (why & SRVST_FAREND) ? (why & SRVST_NEAREND) ? "both ends" : "far end" : "near end");
    				goto tryanotherpos;
    			}
    		}
    #endif
    
    		/* Mark the channel as resetting and restart it */
    		pri->pvts[pri->resetpos]->resetting = 1;
    		pri_reset(pri->pri, PVT_TO_CHANNEL(pri->pvts[pri->resetpos]));
    	} else {
    		pri->resetting = 0;
    		time(&pri->lastreset);
    	}
    	return 0;
    }
    
    static int pri_find_empty_chan(struct sig_pri_pri *pri, int backwards)
    {
    	int x;
    	if (backwards)
    		x = pri->numchans;
    	else
    		x = 0;
    	for (;;) {
    		if (backwards && (x < 0))
    			break;
    		if (!backwards && (x >= pri->numchans))
    			break;
    		if (pri->pvts[x] && !pri->pvts[x]->inalarm && !pri->pvts[x]->owner) {
    
    			ast_debug(1, "Found empty available channel %d/%d\n",
    
    				pri->pvts[x]->logicalspan, pri->pvts[x]->prioffset);
    			return x;
    		}
    		if (backwards)
    			x--;
    		else
    			x++;
    	}
    	return -1;
    }
    
    static void *do_idle_thread(void *vchan)
    {
    	struct ast_channel *chan = vchan;
    	struct sig_pri_chan *pvt = chan->tech_pvt;
    	struct ast_frame *f;
    	char ex[80];
    	/* Wait up to 30 seconds for an answer */
    	int newms, ms = 30000;
    	ast_verb(3, "Initiating idle call on channel %s\n", chan->name);
    	snprintf(ex, sizeof(ex), "%d/%s", pvt->channel, pvt->pri->idledial);
    	if (ast_call(chan, ex, 0)) {
    		ast_log(LOG_WARNING, "Idle dial failed on '%s' to '%s'\n", chan->name, ex);
    		ast_hangup(chan);
    		return NULL;
    	}
    	while ((newms = ast_waitfor(chan, ms)) > 0) {
    		f = ast_read(chan);
    		if (!f) {
    			/* Got hangup */
    			break;
    		}
    		if (f->frametype == AST_FRAME_CONTROL) {
    			switch (f->subclass) {
    			case AST_CONTROL_ANSWER:
    				/* Launch the PBX */
    				ast_copy_string(chan->exten, pvt->pri->idleext, sizeof(chan->exten));
    				ast_copy_string(chan->context, pvt->pri->idlecontext, sizeof(chan->context));
    				chan->priority = 1;
    				ast_verb(4, "Idle channel '%s' answered, sending to %s@%s\n", chan->name, chan->exten, chan->context);
    				ast_pbx_run(chan);
    				/* It's already hungup, return immediately */
    				return NULL;
    			case AST_CONTROL_BUSY:
    				ast_verb(4, "Idle channel '%s' busy, waiting...\n", chan->name);
    				break;
    			case AST_CONTROL_CONGESTION:
    				ast_verb(4, "Idle channel '%s' congested, waiting...\n", chan->name);
    				break;
    			};
    		}
    		ast_frfree(f);
    		ms = newms;
    	}
    	/* Hangup the channel since nothing happend */
    	ast_hangup(chan);
    	return NULL;
    }
    
    static void *pri_ss_thread(void *data)
    {
    	struct sig_pri_chan *p = data;
    	struct ast_channel *chan = p->owner;
    	char exten[AST_MAX_EXTENSION];
    	int res;
    	int len;
    	int timeout;
    
    
    	if (!chan) {
    		/* We lost the owner before we could get started. */
    		return NULL;
    	}
    
    
    	/*
    	 * In the bizarre case where the channel has become a zombie before we
    	 * even get started here, abort safely.
    	 */
    
    		ast_log(LOG_WARNING, "Channel became a zombie before simple switch could be started (%s)\n", chan->name);
    		ast_hangup(chan);
    		return NULL;
    	}
    
    	ast_verb(3, "Starting simple switch on '%s'\n", chan->name);
    
    
    	sig_pri_dsp_reset_and_flush_digits(p);
    
    
    	/* Now loop looking for an extension */
    	ast_copy_string(exten, p->exten, sizeof(exten));
    	len = strlen(exten);
    	res = 0;
    	while ((len < AST_MAX_EXTENSION-1) && ast_matchmore_extension(chan, chan->context, exten, 1, p->cid_num)) {
    		if (len && !ast_ignore_pattern(chan->context, exten))
    			sig_pri_play_tone(p, -1);
    		else
    			sig_pri_play_tone(p, SIG_PRI_TONE_DIALTONE);
    		if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num))
    			timeout = pri_matchdigittimeout;
    		else
    			timeout = pri_gendigittimeout;
    		res = ast_waitfordigit(chan, timeout);
    		if (res < 0) {
    			ast_log(LOG_DEBUG, "waitfordigit returned < 0...\n");
    			ast_hangup(chan);
    			return NULL;
    		} else if (res) {
    			exten[len++] = res;
    			exten[len] = '\0';
    		} else
    			goto exit;
    	}
    	/* if no extension was received ('unspecified') on overlap call, use the 's' extension */
    	if (ast_strlen_zero(exten)) {
    		ast_verb(3, "Going to extension s|1 because of empty extension received on overlap call\n");
    		exten[0] = 's';
    		exten[1] = '\0';
    
    	} else {
    		if (chan->cid.cid_dnid) {
    			ast_free(chan->cid.cid_dnid);
    		}
    		chan->cid.cid_dnid = ast_strdup(exten);
    
    	}
    	sig_pri_play_tone(p, -1);
    	if (ast_exists_extension(chan, chan->context, exten, 1, p->cid_num)) {
    		/* Start the real PBX */
    		ast_copy_string(chan->exten, exten, sizeof(chan->exten));
    
    		sig_pri_dsp_reset_and_flush_digits(p);
    
    		sig_pri_set_echocanceller(p, 1);
    		ast_setstate(chan, AST_STATE_RING);
    		res = ast_pbx_run(chan);
    		if (res) {
    			ast_log(LOG_WARNING, "PBX exited non-zero!\n");
    		}
    	} else {
    		ast_log(LOG_DEBUG, "No such possible extension '%s' in context '%s'\n", exten, chan->context);
    		chan->hangupcause = AST_CAUSE_UNALLOCATED;
    		ast_hangup(chan);
    		p->exten[0] = '\0';
    		/* Since we send release complete here, we won't get one */
    		p->call = NULL;
    	}
    	return NULL;
    
    exit:
    	res = sig_pri_play_tone(p, SIG_PRI_TONE_CONGESTION);
    	if (res < 0)
    
    		ast_log(LOG_WARNING, "Unable to play congestion tone on channel %d\n", p->channel);
    
    	ast_hangup(chan);
    	return NULL;
    }
    
    void pri_event_alarm(struct sig_pri_pri *pri, int index, int before_start_pri)
    {
    	pri->dchanavail[index] &= ~(DCHAN_NOTINALARM | DCHAN_UP);
    	if (!before_start_pri)
    		pri_find_dchan(pri);
    }
    
    void pri_event_noalarm(struct sig_pri_pri *pri, int index, int before_start_pri)
    {
    	pri->dchanavail[index] |= DCHAN_NOTINALARM;
    	if (!before_start_pri)
    		pri_restart(pri->dchans[index]);
    }