|
|
|
@ -330,399 +330,142 @@ static void oh323_destroy(struct oh323_pvt *pvt)
|
|
|
|
|
ast_mutex_unlock(&iflock);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct oh323_alias *build_alias(char *name, struct ast_variable *v)
|
|
|
|
|
/**
|
|
|
|
|
* Send (play) the specified digit to the channel.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static int oh323_digit(struct ast_channel *c, char digit)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_alias *alias;
|
|
|
|
|
|
|
|
|
|
alias = (struct oh323_alias *)malloc(sizeof(struct oh323_alias));
|
|
|
|
|
if (alias) {
|
|
|
|
|
memset(alias, 0, sizeof(struct oh323_alias));
|
|
|
|
|
strncpy(alias->name, name, sizeof(alias->name) - 1);
|
|
|
|
|
while (v) {
|
|
|
|
|
if (!strcasecmp(v->name, "e164")) {
|
|
|
|
|
strncpy(alias->e164, v->value, sizeof(alias->e164) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "prefix")) {
|
|
|
|
|
strncpy(alias->prefix, v->value, sizeof(alias->prefix) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "context")) {
|
|
|
|
|
strncpy(alias->context, v->value, sizeof(alias->context) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "secret")) {
|
|
|
|
|
strncpy(alias->secret, v->value, sizeof(alias->secret) - 1);
|
|
|
|
|
} else {
|
|
|
|
|
if (strcasecmp(v->value, "h323")) {
|
|
|
|
|
ast_log(LOG_WARNING, "Keyword %s does not make sense in type=h323\n", v->value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
v = v->next;
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
char *token;
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Sending digit %c on %s\n", digit, c->name);
|
|
|
|
|
if (!pvt)
|
|
|
|
|
return -1;
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
if (pvt->rtp && (pvt->dtmfmode & H323_DTMF_RFC2833)) {
|
|
|
|
|
ast_rtp_senddigit(pvt->rtp, digit);
|
|
|
|
|
}
|
|
|
|
|
/* If in-band DTMF is desired, send that */
|
|
|
|
|
if ((pvt->dtmfmode & H323_DTMF_INBAND)) {
|
|
|
|
|
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
h323_send_tone(token, digit);
|
|
|
|
|
if (token)
|
|
|
|
|
free(token);
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
}
|
|
|
|
|
return alias;
|
|
|
|
|
else
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct oh323_user *build_user(char *name, struct ast_variable *v)
|
|
|
|
|
/**
|
|
|
|
|
* Make a call over the specified channel to the specified
|
|
|
|
|
* destination.
|
|
|
|
|
* Returns -1 on error, 0 on success.
|
|
|
|
|
*/
|
|
|
|
|
static int oh323_call(struct ast_channel *c, char *dest, int timeout)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_user *user;
|
|
|
|
|
int format;
|
|
|
|
|
int res = 0;
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt;
|
|
|
|
|
char addr[INET_ADDRSTRLEN];
|
|
|
|
|
char called_addr[1024];
|
|
|
|
|
|
|
|
|
|
user = (struct oh323_user *)malloc(sizeof(struct oh323_user));
|
|
|
|
|
if (user) {
|
|
|
|
|
memset(user, 0, sizeof(struct oh323_user));
|
|
|
|
|
strncpy(user->name, name, sizeof(user->name) - 1);
|
|
|
|
|
memcpy(&user->options, &global_options, sizeof(user->options));
|
|
|
|
|
user->capability = capability;
|
|
|
|
|
/* set a native brigding default value */
|
|
|
|
|
user->bridge = bridging;
|
|
|
|
|
/* and default context */
|
|
|
|
|
strncpy(user->context, default_context, sizeof(user->context) - 1);
|
|
|
|
|
while(v) {
|
|
|
|
|
if (!strcasecmp(v->name, "context")) {
|
|
|
|
|
strncpy(user->context, v->value, sizeof(user->context) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "bridge")) {
|
|
|
|
|
user->bridge = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "nat")) {
|
|
|
|
|
user->nat = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noFastStart")) {
|
|
|
|
|
user->options.noFastStart = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
|
|
|
|
|
user->options.noH245Tunneling = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
|
|
|
|
|
user->options.noSilenceSuppression = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "secret")) {
|
|
|
|
|
strncpy(user->secret, v->value, sizeof(user->secret) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "callerid")) {
|
|
|
|
|
strncpy(user->callerid, v->value, sizeof(user->callerid) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "accountcode")) {
|
|
|
|
|
strncpy(user->accountcode, v->value, sizeof(user->accountcode) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_setup")) {
|
|
|
|
|
int progress_setup = atoi(v->value);
|
|
|
|
|
if ((progress_setup != 0) &&
|
|
|
|
|
(progress_setup != 1) &&
|
|
|
|
|
(progress_setup != 3) &&
|
|
|
|
|
(progress_setup != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
|
|
|
|
|
progress_setup = 0;
|
|
|
|
|
if (h323debug) {
|
|
|
|
|
ast_log(LOG_DEBUG, "Calling to %s on %s\n", dest, c->name);
|
|
|
|
|
}
|
|
|
|
|
user->options.progress_setup = progress_setup;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_alert")) {
|
|
|
|
|
int progress_alert = atoi(v->value);
|
|
|
|
|
if ((progress_alert != 0) &&
|
|
|
|
|
(progress_alert != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalud value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
|
|
|
|
|
progress_alert = 0;
|
|
|
|
|
if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Line is already in use (%s)\n", c->name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
user->options.progress_alert = progress_alert;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_audio")) {
|
|
|
|
|
user->options.progress_audio = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfcodec")) {
|
|
|
|
|
user->options.dtmfcodec = atoi(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfmode")) {
|
|
|
|
|
if (!strcasecmp(v->value, "inband")) {
|
|
|
|
|
user->dtmfmode = H323_DTMF_INBAND;
|
|
|
|
|
} else if (!strcasecmp(v->value, "rfc2833")) {
|
|
|
|
|
user->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
if (usingGk) {
|
|
|
|
|
if (ast_strlen_zero(pvt->exten)) {
|
|
|
|
|
strncpy(called_addr, dest, sizeof(called_addr));
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_WARNING, "Unknown DTMF Mode %s, using RFC2833\n", v->value);
|
|
|
|
|
user->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s@%s", pvt->exten, dest);
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "allow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
user->capability |= format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "disallow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
|
|
|
|
|
ast_inet_ntoa(addr, sizeof(addr), pvt->sa.sin_addr);
|
|
|
|
|
res = htons(pvt->sa.sin_port);
|
|
|
|
|
if (ast_strlen_zero(pvt->exten)) {
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s:%d", addr, res);
|
|
|
|
|
} else {
|
|
|
|
|
user->capability &= ~format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "host")) {
|
|
|
|
|
if (!strcasecmp(v->value, "dynamic")) {
|
|
|
|
|
ast_log(LOG_ERROR, "A dynamic host on a type=user does not make any sense\n");
|
|
|
|
|
free(user);
|
|
|
|
|
return NULL;
|
|
|
|
|
} else if (ast_get_ip(&user->addr, v->value)) {
|
|
|
|
|
free(user);
|
|
|
|
|
return NULL;
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, res);
|
|
|
|
|
}
|
|
|
|
|
/* Let us know we need to use ip authentication */
|
|
|
|
|
user->host = 1;
|
|
|
|
|
} else if (!strcasecmp(v->name, "amaflags")) {
|
|
|
|
|
format = ast_cdr_amaflags2int(v->value);
|
|
|
|
|
if (format < 0) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
|
|
|
|
|
} else {
|
|
|
|
|
user->amaflags = format;
|
|
|
|
|
}
|
|
|
|
|
/* make sure null terminated */
|
|
|
|
|
called_addr[sizeof(called_addr) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
if (c->cid.cid_num) {
|
|
|
|
|
strncpy(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num));
|
|
|
|
|
}
|
|
|
|
|
v = v->next;
|
|
|
|
|
if (c->cid.cid_name) {
|
|
|
|
|
strncpy(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* indicate that this is an outgoing call */
|
|
|
|
|
pvt->outgoing = 1;
|
|
|
|
|
|
|
|
|
|
ast_log(LOG_DEBUG, "Placing outgoing call to %s, %d\n", called_addr, pvt->options.dtmfcodec);
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
res = h323_make_call(called_addr, &(pvt->cd), &pvt->options);
|
|
|
|
|
if (res) {
|
|
|
|
|
ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
return user;
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct oh323_peer *build_peer(char *name, struct ast_variable *v)
|
|
|
|
|
static int oh323_answer(struct ast_channel *c)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_peer *peer;
|
|
|
|
|
struct oh323_peer *prev;
|
|
|
|
|
struct ast_ha *oldha = NULL;
|
|
|
|
|
int found=0;
|
|
|
|
|
int format;
|
|
|
|
|
int res;
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
char *token;
|
|
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
|
ast_mutex_lock(&peerl.lock);
|
|
|
|
|
peer = peerl.peers;
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Answering on %s\n", c->name);
|
|
|
|
|
|
|
|
|
|
while(peer) {
|
|
|
|
|
if (!strcasecmp(peer->name, name)) {
|
|
|
|
|
break;
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
res = h323_answering_call(token, 0);
|
|
|
|
|
if (token)
|
|
|
|
|
free(token);
|
|
|
|
|
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
if (c->_state != AST_STATE_UP) {
|
|
|
|
|
ast_setstate(c, AST_STATE_UP);
|
|
|
|
|
}
|
|
|
|
|
prev = peer;
|
|
|
|
|
peer = peer->next;
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (peer) {
|
|
|
|
|
found++;
|
|
|
|
|
/* Already in the list, remove it and it will be added back (or FREE'd) */
|
|
|
|
|
if (prev) {
|
|
|
|
|
prev->next = peer->next;
|
|
|
|
|
} else {
|
|
|
|
|
peerl.peers = peer->next;
|
|
|
|
|
static int oh323_hangup(struct ast_channel *c)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
int needcancel = 0;
|
|
|
|
|
int q931cause = AST_CAUSE_NORMAL_CLEARING;
|
|
|
|
|
char *call_token;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Hanging up call %s\n", c->name);
|
|
|
|
|
|
|
|
|
|
if (!c->tech_pvt) {
|
|
|
|
|
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_unlock(&peerl.lock);
|
|
|
|
|
} else {
|
|
|
|
|
ast_mutex_unlock(&peerl.lock);
|
|
|
|
|
peer = (struct oh323_peer*)malloc(sizeof(struct oh323_peer));
|
|
|
|
|
if (peer)
|
|
|
|
|
memset(peer, 0, sizeof(struct oh323_peer));
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
/* Determine how to disconnect */
|
|
|
|
|
if (pvt->owner != c) {
|
|
|
|
|
ast_log(LOG_WARNING, "Huh? We aren't the owner?\n");
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (peer) {
|
|
|
|
|
if (!found) {
|
|
|
|
|
strncpy(peer->name, name, sizeof(peer->name) - 1);
|
|
|
|
|
peer->addr.sin_port = htons(h323_signalling_port);
|
|
|
|
|
peer->addr.sin_family = AF_INET;
|
|
|
|
|
}
|
|
|
|
|
oldha = peer->ha;
|
|
|
|
|
peer->ha = NULL;
|
|
|
|
|
peer->addr.sin_family = AF_INET;
|
|
|
|
|
peer->capability = capability;
|
|
|
|
|
peer->dtmfmode = dtmfmode;
|
|
|
|
|
peer->bridge = bridging;
|
|
|
|
|
memcpy(&peer->options, &global_options, sizeof(peer->options));
|
|
|
|
|
|
|
|
|
|
while(v) {
|
|
|
|
|
if (!strcasecmp(v->name, "bridge")) {
|
|
|
|
|
peer->bridge = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "nat")) {
|
|
|
|
|
peer->nat = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noFastStart")) {
|
|
|
|
|
peer->options.noFastStart = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
|
|
|
|
|
peer->options.noH245Tunneling = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
|
|
|
|
|
peer->options.noSilenceSuppression = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_setup")) {
|
|
|
|
|
int progress_setup = atoi(v->value);
|
|
|
|
|
if ((progress_setup != 0) &&
|
|
|
|
|
(progress_setup != 1) &&
|
|
|
|
|
(progress_setup != 3) &&
|
|
|
|
|
(progress_setup != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
|
|
|
|
|
progress_setup = 0;
|
|
|
|
|
}
|
|
|
|
|
peer->options.progress_setup = progress_setup;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_alert")) {
|
|
|
|
|
int progress_alert = atoi(v->value);
|
|
|
|
|
if ((progress_alert != 0) &&
|
|
|
|
|
(progress_alert != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
|
|
|
|
|
progress_alert = 0;
|
|
|
|
|
}
|
|
|
|
|
peer->options.progress_alert = progress_alert;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_audio")) {
|
|
|
|
|
peer->options.progress_audio = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfcodec")) {
|
|
|
|
|
peer->options.dtmfcodec = atoi(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfmode")) {
|
|
|
|
|
if (!strcasecmp(v->value, "inband")) {
|
|
|
|
|
peer->dtmfmode = H323_DTMF_INBAND;
|
|
|
|
|
} else if (!strcasecmp(v->value, "rfc2833")) {
|
|
|
|
|
peer->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_WARNING, "Unknown DTMF Mode %s, using RFC2833\n", v->value);
|
|
|
|
|
peer->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "allow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
peer->capability |= format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "disallow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
peer->capability &= ~format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "host")) {
|
|
|
|
|
if (!strcasecmp(v->value, "dynamic")) {
|
|
|
|
|
ast_log(LOG_ERROR, "Dynamic host configuration not implemented.\n");
|
|
|
|
|
free(peer);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (ast_get_ip(&peer->addr, v->value)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value);
|
|
|
|
|
free(peer);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "port")) {
|
|
|
|
|
peer->addr.sin_port = htons(atoi(v->value));
|
|
|
|
|
}
|
|
|
|
|
v=v->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return peer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Send (play) the specified digit to the channel.
|
|
|
|
|
*
|
|
|
|
|
*/
|
|
|
|
|
static int oh323_digit(struct ast_channel *c, char digit)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
char *token;
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Sending digit %c on %s\n", digit, c->name);
|
|
|
|
|
if (!pvt)
|
|
|
|
|
return -1;
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
if (pvt->rtp && (pvt->dtmfmode & H323_DTMF_RFC2833)) {
|
|
|
|
|
ast_rtp_senddigit(pvt->rtp, digit);
|
|
|
|
|
}
|
|
|
|
|
/* If in-band DTMF is desired, send that */
|
|
|
|
|
if ((pvt->dtmfmode & H323_DTMF_INBAND)) {
|
|
|
|
|
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
h323_send_tone(token, digit);
|
|
|
|
|
if (token)
|
|
|
|
|
free(token);
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Make a call over the specified channel to the specified
|
|
|
|
|
* destination.
|
|
|
|
|
* Returns -1 on error, 0 on success.
|
|
|
|
|
*/
|
|
|
|
|
static int oh323_call(struct ast_channel *c, char *dest, int timeout)
|
|
|
|
|
{
|
|
|
|
|
int res = 0;
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *)c->tech_pvt;
|
|
|
|
|
char addr[INET_ADDRSTRLEN];
|
|
|
|
|
char called_addr[1024];
|
|
|
|
|
|
|
|
|
|
if (h323debug) {
|
|
|
|
|
ast_log(LOG_DEBUG, "Calling to %s on %s\n", dest, c->name);
|
|
|
|
|
}
|
|
|
|
|
if ((c->_state != AST_STATE_DOWN) && (c->_state != AST_STATE_RESERVED)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Line is already in use (%s)\n", c->name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
if (usingGk) {
|
|
|
|
|
if (ast_strlen_zero(pvt->exten)) {
|
|
|
|
|
strncpy(called_addr, dest, sizeof(called_addr));
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s@%s", pvt->exten, dest);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
ast_inet_ntoa(addr, sizeof(addr), pvt->sa.sin_addr);
|
|
|
|
|
res = htons(pvt->sa.sin_port);
|
|
|
|
|
if (ast_strlen_zero(pvt->exten)) {
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s:%d", addr, res);
|
|
|
|
|
} else {
|
|
|
|
|
snprintf(called_addr, sizeof(called_addr), "%s@%s:%d", pvt->exten, addr, res);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* make sure null terminated */
|
|
|
|
|
called_addr[sizeof(called_addr) - 1] = '\0';
|
|
|
|
|
|
|
|
|
|
if (c->cid.cid_num) {
|
|
|
|
|
strncpy(pvt->options.cid_num, c->cid.cid_num, sizeof(pvt->options.cid_num));
|
|
|
|
|
}
|
|
|
|
|
if (c->cid.cid_name) {
|
|
|
|
|
strncpy(pvt->options.cid_name, c->cid.cid_name, sizeof(pvt->options.cid_name));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* indicate that this is an outgoing call */
|
|
|
|
|
pvt->outgoing = 1;
|
|
|
|
|
|
|
|
|
|
ast_log(LOG_DEBUG, "Placing outgoing call to %s, %d\n", called_addr, pvt->options.dtmfcodec);
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
res = h323_make_call(called_addr, &(pvt->cd), &pvt->options);
|
|
|
|
|
if (res) {
|
|
|
|
|
ast_log(LOG_NOTICE, "h323_make_call failed(%s)\n", c->name);
|
|
|
|
|
return -1;
|
|
|
|
|
}
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int oh323_answer(struct ast_channel *c)
|
|
|
|
|
{
|
|
|
|
|
int res;
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
char *token;
|
|
|
|
|
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Answering on %s\n", c->name);
|
|
|
|
|
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
token = pvt->cd.call_token ? strdup(pvt->cd.call_token) : NULL;
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
res = h323_answering_call(token, 0);
|
|
|
|
|
if (token)
|
|
|
|
|
free(token);
|
|
|
|
|
|
|
|
|
|
oh323_update_info(c);
|
|
|
|
|
if (c->_state != AST_STATE_UP) {
|
|
|
|
|
ast_setstate(c, AST_STATE_UP);
|
|
|
|
|
}
|
|
|
|
|
return res;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int oh323_hangup(struct ast_channel *c)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_pvt *pvt = (struct oh323_pvt *) c->tech_pvt;
|
|
|
|
|
int needcancel = 0;
|
|
|
|
|
int q931cause = AST_CAUSE_NORMAL_CLEARING;
|
|
|
|
|
char *call_token;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (h323debug)
|
|
|
|
|
ast_log(LOG_DEBUG, "Hanging up call %s\n", c->name);
|
|
|
|
|
|
|
|
|
|
if (!c->tech_pvt) {
|
|
|
|
|
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_lock(&pvt->lock);
|
|
|
|
|
/* Determine how to disconnect */
|
|
|
|
|
if (pvt->owner != c) {
|
|
|
|
|
ast_log(LOG_WARNING, "Huh? We aren't the owner?\n");
|
|
|
|
|
ast_mutex_unlock(&pvt->lock);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
if (!c || (c->_state != AST_STATE_UP)) {
|
|
|
|
|
needcancel = 1;
|
|
|
|
|
if (!c || (c->_state != AST_STATE_UP)) {
|
|
|
|
|
needcancel = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pvt->owner = NULL;
|
|
|
|
@ -1985,6 +1728,263 @@ static struct ast_cli_entry cli_hangup_call =
|
|
|
|
|
static struct ast_cli_entry cli_show_tokens =
|
|
|
|
|
{ { "h.323", "show", "tokens", NULL }, h323_tokens_show, "Show all active call tokens", show_tokens_usage };
|
|
|
|
|
|
|
|
|
|
static struct oh323_alias *build_alias(char *name, struct ast_variable *v)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_alias *alias;
|
|
|
|
|
|
|
|
|
|
alias = (struct oh323_alias *)malloc(sizeof(struct oh323_alias));
|
|
|
|
|
if (alias) {
|
|
|
|
|
memset(alias, 0, sizeof(struct oh323_alias));
|
|
|
|
|
strncpy(alias->name, name, sizeof(alias->name) - 1);
|
|
|
|
|
while (v) {
|
|
|
|
|
if (!strcasecmp(v->name, "e164")) {
|
|
|
|
|
strncpy(alias->e164, v->value, sizeof(alias->e164) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "prefix")) {
|
|
|
|
|
strncpy(alias->prefix, v->value, sizeof(alias->prefix) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "context")) {
|
|
|
|
|
strncpy(alias->context, v->value, sizeof(alias->context) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "secret")) {
|
|
|
|
|
strncpy(alias->secret, v->value, sizeof(alias->secret) - 1);
|
|
|
|
|
} else {
|
|
|
|
|
if (strcasecmp(v->value, "h323")) {
|
|
|
|
|
ast_log(LOG_WARNING, "Keyword %s does not make sense in type=h323\n", v->value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
v = v->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return alias;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct oh323_user *build_user(char *name, struct ast_variable *v)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_user *user;
|
|
|
|
|
int format;
|
|
|
|
|
|
|
|
|
|
user = (struct oh323_user *)malloc(sizeof(struct oh323_user));
|
|
|
|
|
if (user) {
|
|
|
|
|
memset(user, 0, sizeof(struct oh323_user));
|
|
|
|
|
strncpy(user->name, name, sizeof(user->name) - 1);
|
|
|
|
|
memcpy(&user->options, &global_options, sizeof(user->options));
|
|
|
|
|
user->capability = capability;
|
|
|
|
|
/* set a native brigding default value */
|
|
|
|
|
user->bridge = bridging;
|
|
|
|
|
/* and default context */
|
|
|
|
|
strncpy(user->context, default_context, sizeof(user->context) - 1);
|
|
|
|
|
while(v) {
|
|
|
|
|
if (!strcasecmp(v->name, "context")) {
|
|
|
|
|
strncpy(user->context, v->value, sizeof(user->context) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "bridge")) {
|
|
|
|
|
user->bridge = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "nat")) {
|
|
|
|
|
user->nat = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noFastStart")) {
|
|
|
|
|
user->options.noFastStart = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
|
|
|
|
|
user->options.noH245Tunneling = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
|
|
|
|
|
user->options.noSilenceSuppression = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "secret")) {
|
|
|
|
|
strncpy(user->secret, v->value, sizeof(user->secret) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "callerid")) {
|
|
|
|
|
strncpy(user->callerid, v->value, sizeof(user->callerid) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "accountcode")) {
|
|
|
|
|
strncpy(user->accountcode, v->value, sizeof(user->accountcode) - 1);
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_setup")) {
|
|
|
|
|
int progress_setup = atoi(v->value);
|
|
|
|
|
if ((progress_setup != 0) &&
|
|
|
|
|
(progress_setup != 1) &&
|
|
|
|
|
(progress_setup != 3) &&
|
|
|
|
|
(progress_setup != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
|
|
|
|
|
progress_setup = 0;
|
|
|
|
|
}
|
|
|
|
|
user->options.progress_setup = progress_setup;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_alert")) {
|
|
|
|
|
int progress_alert = atoi(v->value);
|
|
|
|
|
if ((progress_alert != 0) &&
|
|
|
|
|
(progress_alert != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalud value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
|
|
|
|
|
progress_alert = 0;
|
|
|
|
|
}
|
|
|
|
|
user->options.progress_alert = progress_alert;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_audio")) {
|
|
|
|
|
user->options.progress_audio = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfcodec")) {
|
|
|
|
|
user->options.dtmfcodec = atoi(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfmode")) {
|
|
|
|
|
if (!strcasecmp(v->value, "inband")) {
|
|
|
|
|
user->dtmfmode = H323_DTMF_INBAND;
|
|
|
|
|
} else if (!strcasecmp(v->value, "rfc2833")) {
|
|
|
|
|
user->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_WARNING, "Unknown DTMF Mode %s, using RFC2833\n", v->value);
|
|
|
|
|
user->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "allow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
user->capability |= format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "disallow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
user->capability &= ~format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "host")) {
|
|
|
|
|
if (!strcasecmp(v->value, "dynamic")) {
|
|
|
|
|
ast_log(LOG_ERROR, "A dynamic host on a type=user does not make any sense\n");
|
|
|
|
|
free(user);
|
|
|
|
|
return NULL;
|
|
|
|
|
} else if (ast_get_ip(&user->addr, v->value)) {
|
|
|
|
|
free(user);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
/* Let us know we need to use ip authentication */
|
|
|
|
|
user->host = 1;
|
|
|
|
|
} else if (!strcasecmp(v->name, "amaflags")) {
|
|
|
|
|
format = ast_cdr_amaflags2int(v->value);
|
|
|
|
|
if (format < 0) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid AMA Flags: %s at line %d\n", v->value, v->lineno);
|
|
|
|
|
} else {
|
|
|
|
|
user->amaflags = format;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
v = v->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return user;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static struct oh323_peer *build_peer(char *name, struct ast_variable *v)
|
|
|
|
|
{
|
|
|
|
|
struct oh323_peer *peer;
|
|
|
|
|
struct oh323_peer *prev;
|
|
|
|
|
struct ast_ha *oldha = NULL;
|
|
|
|
|
int found=0;
|
|
|
|
|
int format;
|
|
|
|
|
|
|
|
|
|
prev = NULL;
|
|
|
|
|
ast_mutex_lock(&peerl.lock);
|
|
|
|
|
peer = peerl.peers;
|
|
|
|
|
|
|
|
|
|
while(peer) {
|
|
|
|
|
if (!strcasecmp(peer->name, name)) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
prev = peer;
|
|
|
|
|
peer = peer->next;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (peer) {
|
|
|
|
|
found++;
|
|
|
|
|
/* Already in the list, remove it and it will be added back (or FREE'd) */
|
|
|
|
|
if (prev) {
|
|
|
|
|
prev->next = peer->next;
|
|
|
|
|
} else {
|
|
|
|
|
peerl.peers = peer->next;
|
|
|
|
|
}
|
|
|
|
|
ast_mutex_unlock(&peerl.lock);
|
|
|
|
|
} else {
|
|
|
|
|
ast_mutex_unlock(&peerl.lock);
|
|
|
|
|
peer = (struct oh323_peer*)malloc(sizeof(struct oh323_peer));
|
|
|
|
|
if (peer)
|
|
|
|
|
memset(peer, 0, sizeof(struct oh323_peer));
|
|
|
|
|
}
|
|
|
|
|
if (peer) {
|
|
|
|
|
if (!found) {
|
|
|
|
|
strncpy(peer->name, name, sizeof(peer->name) - 1);
|
|
|
|
|
peer->addr.sin_port = htons(h323_signalling_port);
|
|
|
|
|
peer->addr.sin_family = AF_INET;
|
|
|
|
|
}
|
|
|
|
|
oldha = peer->ha;
|
|
|
|
|
peer->ha = NULL;
|
|
|
|
|
peer->addr.sin_family = AF_INET;
|
|
|
|
|
peer->capability = capability;
|
|
|
|
|
peer->dtmfmode = dtmfmode;
|
|
|
|
|
peer->bridge = bridging;
|
|
|
|
|
memcpy(&peer->options, &global_options, sizeof(peer->options));
|
|
|
|
|
|
|
|
|
|
while(v) {
|
|
|
|
|
if (!strcasecmp(v->name, "bridge")) {
|
|
|
|
|
peer->bridge = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "nat")) {
|
|
|
|
|
peer->nat = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noFastStart")) {
|
|
|
|
|
peer->options.noFastStart = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noH245Tunneling")) {
|
|
|
|
|
peer->options.noH245Tunneling = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "noSilenceSuppression")) {
|
|
|
|
|
peer->options.noSilenceSuppression = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_setup")) {
|
|
|
|
|
int progress_setup = atoi(v->value);
|
|
|
|
|
if ((progress_setup != 0) &&
|
|
|
|
|
(progress_setup != 1) &&
|
|
|
|
|
(progress_setup != 3) &&
|
|
|
|
|
(progress_setup != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_setup at line %d, assuming 0\n", progress_setup, v->lineno);
|
|
|
|
|
progress_setup = 0;
|
|
|
|
|
}
|
|
|
|
|
peer->options.progress_setup = progress_setup;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_alert")) {
|
|
|
|
|
int progress_alert = atoi(v->value);
|
|
|
|
|
if ((progress_alert != 0) &&
|
|
|
|
|
(progress_alert != 8)) {
|
|
|
|
|
ast_log(LOG_WARNING, "Invalid value %d for progress_alert at line %d, assuming 0\n", progress_alert, v->lineno);
|
|
|
|
|
progress_alert = 0;
|
|
|
|
|
}
|
|
|
|
|
peer->options.progress_alert = progress_alert;
|
|
|
|
|
} else if (!strcasecmp(v->name, "progress_audio")) {
|
|
|
|
|
peer->options.progress_audio = ast_true(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfcodec")) {
|
|
|
|
|
peer->options.dtmfcodec = atoi(v->value);
|
|
|
|
|
} else if (!strcasecmp(v->name, "dtmfmode")) {
|
|
|
|
|
if (!strcasecmp(v->value, "inband")) {
|
|
|
|
|
peer->dtmfmode = H323_DTMF_INBAND;
|
|
|
|
|
} else if (!strcasecmp(v->value, "rfc2833")) {
|
|
|
|
|
peer->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
} else {
|
|
|
|
|
ast_log(LOG_WARNING, "Unknown DTMF Mode %s, using RFC2833\n", v->value);
|
|
|
|
|
peer->dtmfmode = H323_DTMF_RFC2833;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "allow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot allow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
peer->capability |= format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "disallow")) {
|
|
|
|
|
format = ast_getformatbyname(v->value);
|
|
|
|
|
if (format < 1) {
|
|
|
|
|
ast_log(LOG_WARNING, "Cannot disallow unknown format '%s'\n", v->value);
|
|
|
|
|
} else {
|
|
|
|
|
peer->capability &= ~format;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "host")) {
|
|
|
|
|
if (!strcasecmp(v->value, "dynamic")) {
|
|
|
|
|
ast_log(LOG_ERROR, "Dynamic host configuration not implemented.\n");
|
|
|
|
|
free(peer);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
if (ast_get_ip(&peer->addr, v->value)) {
|
|
|
|
|
ast_log(LOG_ERROR, "Could not determine IP for %s\n", v->value);
|
|
|
|
|
free(peer);
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
} else if (!strcasecmp(v->name, "port")) {
|
|
|
|
|
peer->addr.sin_port = htons(atoi(v->value));
|
|
|
|
|
}
|
|
|
|
|
v=v->next;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return peer;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int reload_config(void)
|
|
|
|
|
{
|
|
|
|
|
int format;
|
|
|
|
|