Newer
Older
ast_mutex_unlock(&p->lock);
/*! \brief sip_call: Initiate SIP call from PBX
* used from the dial() application */
static int sip_call(struct ast_channel *ast, char *dest, int timeout)
{
int res;
struct sip_pvt *p;
struct varshead *headp;
struct ast_var_t *current;
Kevin P. Fleming
committed
p = ast->tech_pvt;
if ((ast->_state != AST_STATE_DOWN) && (ast->_state != AST_STATE_RESERVED)) {
ast_log(LOG_WARNING, "sip_call called on %s, neither down nor reserved\n", ast->name);
return -1;
}
Kevin P. Fleming
committed
/* Check whether there is vxml_url, distinctive ring variables */
headp=&ast->varshead;
AST_LIST_TRAVERSE(headp,current,entries) {
/* Check whether there is a VXML_URL variable */
Kevin P. Fleming
committed
if (!p->options->vxml_url && !strcasecmp(ast_var_name(current), "VXML_URL")) {
p->options->vxml_url = ast_var_value(current);
} else if (!p->options->uri_options && !strcasecmp(ast_var_name(current), "SIP_URI_OPTIONS")) {
p->options->uri_options = ast_var_value(current);
Kevin P. Fleming
committed
} else if (!p->options->distinctive_ring && !strcasecmp(ast_var_name(current), "ALERT_INFO")) {
/* Check whether there is a ALERT_INFO variable */
Kevin P. Fleming
committed
p->options->distinctive_ring = ast_var_value(current);
} else if (!p->options->addsipheaders && !strncasecmp(ast_var_name(current), "SIPADDHEADER", strlen("SIPADDHEADER"))) {
/* Check whether there is a variable with a name starting with SIPADDHEADER */
Kevin P. Fleming
committed
p->options->addsipheaders = 1;
Kevin P. Fleming
committed
else if (!p->options->osptoken && !strcasecmp(ast_var_name(current), "OSPTOKEN")) {
p->options->osptoken = ast_var_value(current);
} else if (!osphandle && !strcasecmp(ast_var_name(current), "OSPHANDLE")) {
osphandle = ast_var_value(current);
}
#endif
ast_set_flag(p, SIP_OUTGOING);
Kevin P. Fleming
committed
if (!p->options->osptoken || !osphandle || (sscanf(osphandle, "%d", &p->osphandle) != 1)) {
/* Force Disable OSP support */
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "Disabling OSP support for this call. osptoken = %s, osphandle = %s\n", p->options->osptoken, osphandle);
Kevin P. Fleming
committed
p->options->osptoken = NULL;
osphandle = NULL;
p->osphandle = -1;
}
#endif
ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username);
Kevin P. Fleming
committed
res = update_call_counter(p, INC_CALL_LIMIT);
p->callingpres = ast->cid.cid_pres;
transmit_invite(p, SIP_INVITE, 1, 2);
if (p->maxtime) {
/* Initialize auto-congest time */
p->initid = ast_sched_add(sched, p->maxtime * 4, auto_congest, p);
/*! \brief sip_registry_destroy: Destroy registry object */
/* Objects created with the register= statement in static configuration */
Mark Spencer
committed
static void sip_registry_destroy(struct sip_registry *reg)
{
/* Really delete */
if (reg->call) {
/* Clear registry before destroying to ensure
we don't get reentered trying to grab the registry lock */
reg->call->registry = NULL;
sip_destroy(reg->call);
}
if (reg->expire > -1)
ast_sched_del(sched, reg->expire);
if (reg->timeout > -1)
ast_sched_del(sched, reg->timeout);
ast_string_field_free_all(reg);
Mark Spencer
committed
regobjs--;
free(reg);
}
Olle Johansson
committed
/*! \brief __sip_destroy: Execute destrucion of SIP dialog structure, release memory */
static void __sip_destroy(struct sip_pvt *p, int lockowner)
if (sip_debug_test_pvt(p))
Olle Johansson
committed
ast_verbose("Destroying SIP dialog '%s' Method: %s\n", p->callid, sip_methods[p->method].text);
if (dumphistory)
sip_dump_history(p);
Kevin P. Fleming
committed
if (p->options)
free(p->options);
if (p->stateid > -1)
ast_extension_state_del(p->stateid, NULL);
if (p->initid > -1)
ast_sched_del(sched, p->initid);
if (p->autokillid > -1)
ast_sched_del(sched, p->autokillid);
if (p->rtp) {
ast_rtp_destroy(p->rtp);
}
if (p->vrtp) {
ast_rtp_destroy(p->vrtp);
}
if (p->route) {
free_old_route(p->route);
p->route = NULL;
}
Martin Pycko
committed
if (p->registry) {
Mark Spencer
committed
if (p->registry->call == p)
p->registry->call = NULL;
Olle Johansson
committed
ASTOBJ_UNREF(p->registry, sip_registry_destroy);
Martin Pycko
committed
}
Kevin P. Fleming
committed
/* Unlink us from the owner if we have one */
if (p->owner) {
ast_mutex_lock(&p->owner->lock);
Olle Johansson
committed
if (option_debug)
ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
p->owner->tech_pvt = NULL;
ast_mutex_unlock(&p->owner->lock);
Mark Spencer
committed
/* Clear history */
Olle Johansson
committed
if (p->history) {
while(!AST_LIST_EMPTY(p->history)) {
struct sip_history *hist = AST_LIST_FIRST(p->history);
AST_LIST_REMOVE_HEAD(p->history, list);
free(hist);
}
free(p->history);
p->history = NULL;
Mark Spencer
committed
}
cur = iflist;
while(cur) {
if (cur == p) {
if (prev)
prev->next = cur->next;
else
iflist = cur->next;
break;
}
prev = cur;
cur = cur->next;
}
if (!cur) {
ast_log(LOG_WARNING, "Trying to destroy \"%s\", not found in dialog list?!?! \n", p->callid);
return;
}
if (p->initid > -1)
ast_sched_del(sched, p->initid);
while((cp = p->packets)) {
p->packets = p->packets->next;
if (cp->retransid > -1) {
ast_sched_del(sched, cp->retransid);
if (p->chanvars) {
ast_variables_destroy(p->chanvars);
p->chanvars = NULL;
}
ast_mutex_destroy(&p->lock);
ast_string_field_free_all(p);
/*! \brief update_call_counter: Handle call_limit for SIP users
* Setting a call-limit will cause calls above the limit not to be accepted.
*
* Remember that for a type=friend, there's one limit for the user and
* another for the peer, not a combined call limit.
* This will cause unexpected behaviour in subscriptions, since a "friend"
* is *two* devices in Asterisk, not one.
*
* Thought: For realtime, we should propably update storage with inuse counter...
*/
Kevin P. Fleming
committed
static int update_call_counter(struct sip_pvt *fup, int event)
Kevin P. Fleming
committed
int *inuse, *call_limit;
int outgoing = ast_test_flag(fup, SIP_OUTGOING);
struct sip_user *u = NULL;
struct sip_peer *p = NULL;
Kevin P. Fleming
committed
if (option_debug > 2)
ast_log(LOG_DEBUG, "Updating call counter for %s call\n", outgoing ? "outgoing" : "incoming");
/* Test if we need to check call limits, in order to avoid
realtime lookups if we do not need it */
if (!ast_test_flag(fup, SIP_CALL_LIMIT))
return 0;
Kevin P. Fleming
committed
ast_copy_string(name, fup->username, sizeof(name));
/* Check the list of users */
if (!outgoing) /* Only check users for incoming calls */
u = find_user(name, 1);
if (u) {
inuse = &u->inUse;
Kevin P. Fleming
committed
call_limit = &u->call_limit;
p = NULL;
} else {
/* Try to find peer */
Kevin P. Fleming
committed
if (!p)
p = find_peer(fup->peername, NULL, 1);
if (p) {
inuse = &p->inUse;
Kevin P. Fleming
committed
call_limit = &p->call_limit;
Kevin P. Fleming
committed
ast_copy_string(name, fup->peername, sizeof(name));
} else {
if (option_debug > 1)
ast_log(LOG_DEBUG, "%s is not a local user, no call limit\n", name);
return 0;
}
/* incoming and outgoing affects the inUse counter */
Kevin P. Fleming
committed
case DEC_CALL_LIMIT:
if ( *inuse > 0 ) {
if (ast_test_flag(fup, SIP_INC_COUNT))
(*inuse)--;
*inuse = 0;
Russell Bryant
committed
if (option_debug > 1 || sipdebug) {
ast_log(LOG_DEBUG, "Call %s %s '%s' removed from call limit %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
}
Kevin P. Fleming
committed
case INC_CALL_LIMIT:
if (*call_limit > 0 ) {
if (*inuse >= *call_limit) {
ast_log(LOG_ERROR, "Call %s %s '%s' rejected due to usage limit of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *call_limit);
if (u)
ASTOBJ_UNREF(u, sip_destroy_user);
else
ASTOBJ_UNREF(p, sip_destroy_peer);
ast_set_flag(fup, SIP_INC_COUNT);
Russell Bryant
committed
if (option_debug > 1 || sipdebug) {
ast_log(LOG_DEBUG, "Call %s %s '%s' is %d out of %d\n", outgoing ? "to" : "from", u ? "user":"peer", name, *inuse, *call_limit);
}
Kevin P. Fleming
committed
ast_log(LOG_ERROR, "update_call_counter(%s, %d) called with no event!\n", name, event);
if (u)
ASTOBJ_UNREF(u, sip_destroy_user);
else
ASTOBJ_UNREF(p, sip_destroy_peer);
/*! \brief sip_destroy: Destroy SIP call structure */
static void sip_destroy(struct sip_pvt *p)
{
ast_mutex_lock(&iflock);
ast_mutex_unlock(&iflock);
static int transmit_response_reliable(struct sip_pvt *p, char *msg, struct sip_request *req, int fatal);
/*! \brief hangup_sip2cause: Convert SIP hangup causes to Asterisk hangup causes */
Martin Pycko
committed
static int hangup_sip2cause(int cause)
{
/* Possible values taken from causes.h */
Olle Johansson
committed
case 401: /* Unauthorized */
return AST_CAUSE_CALL_REJECTED;
case 403: /* Not found */
return AST_CAUSE_CALL_REJECTED;
case 404: /* Not found */
Olle Johansson
committed
case 405: /* Method not allowed */
return AST_CAUSE_INTERWORKING;
case 407: /* Proxy authentication required */
return AST_CAUSE_CALL_REJECTED;
case 408: /* No reaction */
return AST_CAUSE_NO_USER_RESPONSE;
Olle Johansson
committed
case 409: /* Conflict */
return AST_CAUSE_NORMAL_TEMPORARY_FAILURE;
case 410: /* Gone */
return AST_CAUSE_UNALLOCATED;
case 411: /* Length required */
return AST_CAUSE_INTERWORKING;
case 413: /* Request entity too large */
return AST_CAUSE_INTERWORKING;
case 414: /* Request URI too large */
return AST_CAUSE_INTERWORKING;
case 415: /* Unsupported media type */
return AST_CAUSE_INTERWORKING;
case 420: /* Bad extension */
return AST_CAUSE_NO_ROUTE_DESTINATION;
case 480: /* No answer */
Olle Johansson
committed
case 481: /* No answer */
return AST_CAUSE_INTERWORKING;
case 482: /* Loop detected */
return AST_CAUSE_INTERWORKING;
case 483: /* Too many hops */
return AST_CAUSE_NO_ANSWER;
Olle Johansson
committed
case 484: /* Address incomplete */
return AST_CAUSE_INVALID_NUMBER_FORMAT;
case 485: /* Ambigous */
return AST_CAUSE_UNALLOCATED;
case 486: /* Busy everywhere */
Martin Pycko
committed
return AST_CAUSE_BUSY;
Olle Johansson
committed
case 487: /* Request terminated */
return AST_CAUSE_INTERWORKING;
case 488: /* No codecs approved */
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
Olle Johansson
committed
case 491: /* Request pending */
return AST_CAUSE_INTERWORKING;
case 493: /* Undecipherable */
return AST_CAUSE_INTERWORKING;
case 500: /* Server internal failure */
return AST_CAUSE_FAILURE;
case 501: /* Call rejected */
return AST_CAUSE_FACILITY_REJECTED;
case 502:
return AST_CAUSE_DESTINATION_OUT_OF_ORDER;
case 503: /* Service unavailable */
return AST_CAUSE_CONGESTION;
Olle Johansson
committed
case 504: /* Gateway timeout */
return AST_CAUSE_RECOVERY_ON_TIMER_EXPIRE;
case 505: /* SIP version not supported */
return AST_CAUSE_INTERWORKING;
case 600: /* Busy everywhere */
return AST_CAUSE_USER_BUSY;
case 603: /* Decline */
return AST_CAUSE_CALL_REJECTED;
case 604: /* Does not exist anywhere */
return AST_CAUSE_UNALLOCATED;
case 606: /* Not acceptable */
return AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
Martin Pycko
committed
default:
return AST_CAUSE_NORMAL;
}
/* Never reached */
return 0;
}
/*! \brief hangup_cause2sip: Convert Asterisk hangup causes to SIP codes
\verbatim
Possible values from causes.h
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
AST_CAUSE_NOTDEFINED AST_CAUSE_NORMAL AST_CAUSE_BUSY
AST_CAUSE_FAILURE AST_CAUSE_CONGESTION AST_CAUSE_UNALLOCATED
In addition to these, a lot of PRI codes is defined in causes.h
...should we take care of them too ?
Quote RFC 3398
ISUP Cause value SIP response
---------------- ------------
1 unallocated number 404 Not Found
2 no route to network 404 Not found
3 no route to destination 404 Not found
16 normal call clearing --- (*)
17 user busy 486 Busy here
18 no user responding 408 Request Timeout
19 no answer from the user 480 Temporarily unavailable
20 subscriber absent 480 Temporarily unavailable
21 call rejected 403 Forbidden (+)
22 number changed (w/o diagnostic) 410 Gone
22 number changed (w/ diagnostic) 301 Moved Permanently
23 redirection to new destination 410 Gone
26 non-selected user clearing 404 Not Found (=)
27 destination out of order 502 Bad Gateway
28 address incomplete 484 Address incomplete
29 facility rejected 501 Not implemented
31 normal unspecified 480 Temporarily unavailable
Martin Pycko
committed
static char *hangup_cause2sip(int cause)
{
switch(cause)
{
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
case AST_CAUSE_UNALLOCATED: /* 1 */
case AST_CAUSE_NO_ROUTE_DESTINATION: /* 3 IAX2: Can't find extension in context */
case AST_CAUSE_NO_ROUTE_TRANSIT_NET: /* 2 */
return "404 Not Found";
case AST_CAUSE_CONGESTION: /* 34 */
case AST_CAUSE_SWITCH_CONGESTION: /* 42 */
return "503 Service Unavailable";
case AST_CAUSE_NO_USER_RESPONSE: /* 18 */
return "408 Request Timeout";
case AST_CAUSE_NO_ANSWER: /* 19 */
return "480 Temporarily unavailable";
case AST_CAUSE_CALL_REJECTED: /* 21 */
return "403 Forbidden";
case AST_CAUSE_NUMBER_CHANGED: /* 22 */
return "410 Gone";
case AST_CAUSE_NORMAL_UNSPECIFIED: /* 31 */
return "480 Temporarily unavailable";
case AST_CAUSE_INVALID_NUMBER_FORMAT:
return "484 Address incomplete";
case AST_CAUSE_USER_BUSY:
return "486 Busy here";
case AST_CAUSE_FAILURE:
return "500 Server internal failure";
case AST_CAUSE_FACILITY_REJECTED: /* 29 */
return "501 Not Implemented";
case AST_CAUSE_CHAN_NOT_IMPLEMENTED:
return "503 Service Unavailable";
/* Used in chan_iax2 */
case AST_CAUSE_DESTINATION_OUT_OF_ORDER:
return "502 Bad Gateway";
case AST_CAUSE_BEARERCAPABILITY_NOTAVAIL: /* Can't find codec to connect to host */
return "488 Not Acceptable Here";
case AST_CAUSE_NOTDEFINED:
Martin Pycko
committed
default:
ast_log(LOG_DEBUG, "AST hangup cause %d (no match found in SIP)\n", cause);
Martin Pycko
committed
return NULL;
}
Martin Pycko
committed
/* Never reached */
return 0;
}
Olle Johansson
committed
/*! \brief sip_hangup: Hangup SIP call
* Part of PBX interface, called from ast_hangup */
static int sip_hangup(struct ast_channel *ast)
{
struct sip_pvt *p = ast->tech_pvt;
struct ast_flags locflags = {0};
if (!p) {
ast_log(LOG_DEBUG, "Asked to hangup channel not connected\n");
Kevin P. Fleming
committed
ast_log(LOG_DEBUG, "Hangup call %s, SIP callid %s)\n", ast->name, p->callid);
ast_mutex_lock(&p->lock);
#ifdef OSP_SUPPORT
if ((p->osphandle > -1) && (ast->_state == AST_STATE_UP)) {
ast_osp_terminate(p->osphandle, AST_CAUSE_NORMAL, p->ospstart, time(NULL) - p->ospstart);
}
#endif
if (option_debug && sipdebug)
ast_log(LOG_DEBUG, "update_call_counter(%s) - decrement call limit counter on hangup\n", p->username);
Kevin P. Fleming
committed
update_call_counter(p, DEC_CALL_LIMIT);
Kevin P. Fleming
committed
ast_log(LOG_WARNING, "Huh? We aren't the owner? Can't hangup call.\n");
ast_mutex_unlock(&p->lock);
Kevin P. Fleming
committed
/* If the call is not UP, we need to send CANCEL instead of BYE */
if (ast->_state != AST_STATE_UP)
Kevin P. Fleming
committed
p = ast->tech_pvt;
ast->tech_pvt = NULL;
ast_mutex_lock(&usecnt_lock);
usecnt--;
ast_mutex_unlock(&usecnt_lock);
ast_update_use_count();
ast_set_flag(&locflags, SIP_NEEDDESTROY);
Kevin P. Fleming
committed
/* Start the process if it's not already started */
if (!ast_test_flag(p, SIP_ALREADYGONE) && !ast_strlen_zero(p->initreq.data)) {
Kevin P. Fleming
committed
if (needcancel) { /* Outgoing call, not up */
if (ast_test_flag(p, SIP_OUTGOING)) {
transmit_request_with_auth(p, SIP_CANCEL, p->ocseq, 1, 0);
/* Actually don't destroy us yet, wait for the 487 on our original
INVITE, but do set an autodestruct just in case we never get it. */
ast_clear_flag(&locflags, SIP_NEEDDESTROY);
sip_scheddestroy(p, 15000);
/* stop retransmitting an INVITE that has not received a response */
__sip_pretend_ack(p);
if ( p->initid != -1 ) {
/* channel still up - reverse dec of inUse counter
only if the channel is not auto-congested */
Kevin P. Fleming
committed
update_call_counter(p, INC_CALL_LIMIT);
Kevin P. Fleming
committed
} else { /* Incoming call, not up */
Martin Pycko
committed
char *res;
if (ast->hangupcause && ((res = hangup_cause2sip(ast->hangupcause)))) {
transmit_response_reliable(p, res, &p->initreq, 1);
Martin Pycko
committed
} else
transmit_response_reliable(p, "603 Declined", &p->initreq, 1);
Martin Pycko
committed
}
Kevin P. Fleming
committed
} else { /* Call is in UP state, send BYE */
if (!p->pendinginvite) {
/* Send a hangup */
transmit_request_with_auth(p, SIP_BYE, 0, 1, 1);
} else {
/* Note we will need a BYE when this all settles out
but we can't send one while we have "INVITE" outstanding. */
ast_set_flag(p, SIP_PENDINGBYE);
ast_clear_flag(p, SIP_NEEDREINVITE);
}
ast_copy_flags(p, (&locflags), SIP_NEEDDESTROY);
ast_mutex_unlock(&p->lock);
/*! \brief sip_answer: Answer SIP call , send 200 OK on Invite
* Part of PBX interface */
static int sip_answer(struct ast_channel *ast)
{
const char *codec;
struct sip_pvt *p = ast->tech_pvt;
#ifdef OSP_SUPPORT
time(&p->ospstart);
#endif
codec=pbx_builtin_getvar_helper(p->owner,"SIP_CODEC");
if (codec) {
fmt=ast_getformatbyname(codec);
if (fmt) {
ast_log(LOG_NOTICE, "Changing codec to '%s' for this call because of ${SIP_CODEC) variable\n",codec);
if (p->jointcapability & fmt) {
p->jointcapability &= fmt;
p->capability &= fmt;
} else
ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because it is not shared by both ends.\n");
} else ast_log(LOG_NOTICE, "Ignoring ${SIP_CODEC} variable because of unrecognized/not configured codec (check allow/disallow in sip.conf): %s\n",codec);
if (option_debug)
ast_log(LOG_DEBUG, "sip_answer(%s)\n", ast->name);
res = transmit_response_with_sdp(p, "200 OK", &p->initreq, 1);
/*! \brief sip_write: Send frame to media channel (rtp) */
static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
{
struct sip_pvt *p = ast->tech_pvt;
switch (frame->frametype) {
case AST_FRAME_VOICE:
if (!(frame->subclass & ast->nativeformats)) {
ast_log(LOG_WARNING, "Asked to transmit frame type %d, while native formats is %d (read/write = %d/%d)\n",
frame->subclass, ast->nativeformats, ast->readformat, ast->writeformat);
Mark Spencer
committed
return 0;
if (p) {
ast_mutex_lock(&p->lock);
if (p->rtp) {
/* If channel is not up, activate early media session */
if ((ast->_state != AST_STATE_UP) && !ast_test_flag(p, SIP_PROGRESS_SENT) && !ast_test_flag(p, SIP_OUTGOING)) {
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
ast_set_flag(p, SIP_PROGRESS_SENT);
res = ast_rtp_write(p->rtp, frame);
ast_mutex_unlock(&p->lock);
break;
case AST_FRAME_VIDEO:
if (p) {
ast_mutex_lock(&p->lock);
if (p->vrtp) {
if ((ast->_state != AST_STATE_UP) && !ast_test_flag(p, SIP_PROGRESS_SENT) && !ast_test_flag(p, SIP_OUTGOING)) {
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
ast_set_flag(p, SIP_PROGRESS_SENT);
res = ast_rtp_write(p->vrtp, frame);
}
ast_mutex_unlock(&p->lock);
break;
case AST_FRAME_IMAGE:
return 0;
ast_log(LOG_WARNING, "Can't send %d type frames with SIP write\n", frame->frametype);
return 0;
/*! \brief sip_fixup: Fix up a channel: If a channel is consumed, this is called.
Basically update any ->owner links */
Mark Spencer
committed
static int sip_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
struct sip_pvt *p = newchan->tech_pvt;
Mark Spencer
committed
ast_mutex_lock(&p->lock);
if (p->owner != oldchan) {
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
Mark Spencer
committed
ast_mutex_unlock(&p->lock);
Mark Spencer
committed
ast_mutex_unlock(&p->lock);
/*! \brief sip_senddigit: Send DTMF character on SIP channel */
/* within one call, we're able to transmit in many methods simultaneously */
static int sip_senddigit(struct ast_channel *ast, char digit)
{
struct sip_pvt *p = ast->tech_pvt;
switch (ast_test_flag(p, SIP_DTMF)) {
case SIP_DTMF_INFO:
break;
case SIP_DTMF_RFC2833:
if (p->rtp)
ast_rtp_senddigit(p->rtp, digit);
break;
case SIP_DTMF_INBAND:
Kevin P. Fleming
committed
/*! \brief sip_transfer: Transfer SIP call */
Mark Spencer
committed
static int sip_transfer(struct ast_channel *ast, const char *dest)
struct sip_pvt *p = ast->tech_pvt;
if (ast->_state == AST_STATE_RING)
res = sip_sipredirect(p, dest);
else
res = transmit_refer(p, dest);
/*! \brief sip_indicate: Play indication to user
* With SIP a lot of indications is sent as messages, letting the device play
the indication - busy signal, congestion etc */
static int sip_indicate(struct ast_channel *ast, int condition)
{
struct sip_pvt *p = ast->tech_pvt;
int res = 0;
ast_mutex_lock(&p->lock);
switch(condition) {
case AST_CONTROL_RINGING:
if (!ast_test_flag(p, SIP_PROGRESS_SENT) ||
(ast_test_flag(p, SIP_PROG_INBAND) == SIP_PROG_INBAND_NEVER)) {
/* Send 180 ringing if out-of-band seems reasonable */
transmit_response(p, "180 Ringing", &p->initreq);
ast_set_flag(p, SIP_RINGING);
if (ast_test_flag(p, SIP_PROG_INBAND) != SIP_PROG_INBAND_YES)
/* Well, if it's not reasonable, just send in-band */
transmit_response(p, "486 Busy Here", &p->initreq);
ast_set_flag(p, SIP_ALREADYGONE);
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
transmit_response(p, "503 Service Unavailable", &p->initreq);
ast_set_flag(p, SIP_ALREADYGONE);
ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV);
Kevin P. Fleming
committed
if ((ast->_state != AST_STATE_UP) && !ast_test_flag(p, SIP_PROGRESS_SENT) && !ast_test_flag(p, SIP_OUTGOING)) {
transmit_response(p, "100 Trying", &p->initreq);
break;
}
res = -1;
break;
case AST_CONTROL_PROGRESS:
if ((ast->_state != AST_STATE_UP) && !ast_test_flag(p, SIP_PROGRESS_SENT) && !ast_test_flag(p, SIP_OUTGOING)) {
transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
ast_set_flag(p, SIP_PROGRESS_SENT);
case AST_CONTROL_HOLD: /* The other part of the bridge are put on hold */
Russell Bryant
committed
if (sipdebug)
Olle Johansson
committed
ast_log(LOG_DEBUG, "Bridged channel now on hold - %s\n", p->callid);
res = -1;
break;
case AST_CONTROL_UNHOLD: /* The other part of the bridge are back from hold */
Russell Bryant
committed
if (sipdebug)
ast_log(LOG_DEBUG, "Bridged channel is back from hold, let's talk! : %s\n", p->callid);
res = -1;
break;
case AST_CONTROL_VIDUPDATE: /* Request a video frame update */
if (p->vrtp && !ast_test_flag(p, SIP_NOVIDEO)) {
transmit_info_with_vidupdate(p);
res = 0;
} else
res = -1;
break;
ast_log(LOG_WARNING, "Don't know how to indicate condition %d\n", condition);
/*! \brief sip_new: Initiate a call in the SIP channel */
/* called from sip_request_call (calls from the pbx ) */
static struct ast_channel *sip_new(struct sip_pvt *i, int state, const char *title)
int what;
#ifdef OSP_SUPPORT
char iabuf[INET_ADDRSTRLEN];
char peer[MAXHOSTNAMELEN];
#endif
Mark Spencer
committed
ast_mutex_unlock(&i->lock);
/* Don't hold a sip pvt lock while we allocate a channel */
Mark Spencer
committed
ast_mutex_lock(&i->lock);
if (!tmp) {
ast_log(LOG_WARNING, "Unable to allocate SIP channel structure\n");
return NULL;
}
tmp->tech = &sip_tech;
/* Select our native format based on codec preference until we receive
something from another device to the contrary. */
if (i->jointcapability)
what = i->jointcapability;
what = i->capability;
what = global_capability;
tmp->nativeformats = ast_codec_choose(&i->prefs, what, 1) | (i->jointcapability & AST_FORMAT_VIDEO_MASK);
fmt = ast_best_codec(tmp->nativeformats);
if (title)
snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%04x", title, thread_safe_rand() & 0xffff);
else if (strchr(i->fromdomain,':'))
snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%08x", strchr(i->fromdomain,':')+1, (int)(long)(i));
else
snprintf(tmp->name, sizeof(tmp->name), "SIP/%s-%08x", i->fromdomain, (int)(long)(i));
tmp->type = channeltype;
if (ast_test_flag(i, SIP_DTMF) == SIP_DTMF_INBAND) {
i->vad = ast_dsp_new();
ast_dsp_set_features(i->vad, DSP_FEATURE_DTMF_DETECT);
if (relaxdtmf)
ast_dsp_digitmode(i->vad, DSP_DIGITMODE_DTMF | DSP_DIGITMODE_RELAXDTMF);
}
if (i->rtp) {
tmp->fds[0] = ast_rtp_fd(i->rtp);
tmp->fds[1] = ast_rtcp_fd(i->rtp);
}
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
if (i->vrtp) {
tmp->fds[2] = ast_rtp_fd(i->vrtp);
tmp->fds[3] = ast_rtcp_fd(i->vrtp);
}
if (state == AST_STATE_RING)
tmp->rings = 1;
tmp->adsicpe = AST_ADSI_UNAVAILABLE;
tmp->writeformat = fmt;
tmp->rawwriteformat = fmt;
tmp->readformat = fmt;
tmp->rawreadformat = fmt;
tmp->tech_pvt = i;
tmp->callgroup = i->callgroup;
tmp->pickupgroup = i->pickupgroup;
tmp->cid.cid_pres = i->callingpres;
if (!ast_strlen_zero(i->accountcode))
ast_copy_string(tmp->accountcode, i->accountcode, sizeof(tmp->accountcode));
if (i->amaflags)
tmp->amaflags = i->amaflags;
if (!ast_strlen_zero(i->language))
ast_copy_string(tmp->language, i->language, sizeof(tmp->language));
if (!ast_strlen_zero(i->musicclass))
ast_copy_string(tmp->musicclass, i->musicclass, sizeof(tmp->musicclass));
i->owner = tmp;
ast_mutex_lock(&usecnt_lock);
usecnt++;
ast_mutex_unlock(&usecnt_lock);
ast_copy_string(tmp->context, i->context, sizeof(tmp->context));
ast_copy_string(tmp->exten, i->exten, sizeof(tmp->exten));
if (!ast_strlen_zero(i->cid_num))
tmp->cid.cid_num = ast_strdup(i->cid_num);
if (!ast_strlen_zero(i->cid_name))
tmp->cid.cid_name = ast_strdup(i->cid_name);
tmp->cid.cid_rdnis = ast_strdup(i->rdnis);
if (!ast_strlen_zero(i->exten) && strcmp(i->exten, "s"))
tmp->cid.cid_dnid = ast_strdup(i->exten);
tmp->priority = 1;
if (!ast_strlen_zero(i->uri)) {
pbx_builtin_setvar_helper(tmp, "SIPURI", i->uri);
}
if (!ast_strlen_zero(i->domain)) {
pbx_builtin_setvar_helper(tmp, "SIPDOMAIN", i->domain);
}
if (!ast_strlen_zero(i->useragent)) {
pbx_builtin_setvar_helper(tmp, "SIPUSERAGENT", i->useragent);
}
if (!ast_strlen_zero(i->callid)) {
pbx_builtin_setvar_helper(tmp, "SIPCALLID", i->callid);
}
#ifdef OSP_SUPPORT
snprintf(peer, sizeof(peer), "[%s]:%d", ast_inet_ntoa(iabuf, sizeof(iabuf), i->sa.sin_addr), ntohs(i->sa.sin_port));
pbx_builtin_setvar_helper(tmp, "OSPPEER", peer);
#endif
ast_setstate(tmp, state);
if (state != AST_STATE_DOWN) {
Kevin P. Fleming
committed
if (ast_pbx_start(tmp)) {
ast_log(LOG_WARNING, "Unable to start PBX on %s\n", tmp->name);
ast_hangup(tmp);
tmp = NULL;
}
/* Set channel variables for this call from configuration */
for (v = i->chanvars ; v ; v = v->next)
pbx_builtin_setvar_helper(tmp,v->name,v->value);
/*! \brief get_sdp_by_line: Reads one line of SIP message body */
static char* get_sdp_by_line(char* line, char *name, int nameLen)
{
if (strncasecmp(line, name, nameLen) == 0 && line[nameLen] == '=') {
return ast_skip_blanks(line + nameLen + 1);
/*! \brief get_sdp: Gets all kind of SIP message bodies, including SDP,
but the name wrongly applies _only_ sdp */
static char *get_sdp(struct sip_request *req, char *name)
{
int x;
int len = strlen(name);
char *r;
for (x=0; x<req->lines; x++) {
r = get_sdp_by_line(req->line[x], name, len);
if (r[0] != '\0')
return r;
}
return "";
static void sdpLineNum_iterator_init(int* iterator)
{
*iterator = 0;
}
static char* get_sdp_iterate(int* iterator,
struct sip_request *req, char *name)
{
int len = strlen(name);
char *r;
while (*iterator < req->lines) {
r = get_sdp_by_line(req->line[(*iterator)++], name, len);
if (r[0] != '\0')
return r;
}
return "";
static char *find_alias(const char *name, char *_default)
2954
2955
2956
2957
2958
2959
2960
2961
2962
2963
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
for (x=0;x<sizeof(aliases) / sizeof(aliases[0]); x++)
if (!strcasecmp(aliases[x].fullname, name))
return aliases[x].shortname;
return _default;
}
static char *__get_header(struct sip_request *req, char *name, int *start)
{
int pass;
/*
* Technically you can place arbitrary whitespace both before and after the ':' in
* a header, although RFC3261 clearly says you shouldn't before, and place just
* one afterwards. If you shouldn't do it, what absolute idiot decided it was
* a good idea to say you can do it, and if you can do it, why in the hell would.
* you say you shouldn't.
* Anyways, pedanticsipchecking controls whether we allow spaces before ':',
* and we always allow spaces after that for compatibility.
*/
for (pass = 0; name && pass < 2;pass++) {
int x, len = strlen(name);
for (x=*start; x<req->headers; x++) {
if (!strncasecmp(req->header[x], name, len)) {
char *r = req->header[x] + len; /* skip name */
if (pedanticsipchecking)
r = ast_skip_blanks(r);
if (*r == ':') {
return ast_skip_blanks(r+1);
}
}
}
if (pass == 0) /* Try aliases */
name = find_alias(name, NULL);
}
/* Don't return NULL, so get_header is always a valid pointer */
return "";
}
/*! \brief get_header: Get header from SIP request */
static char *get_header(struct sip_request *req, char *name)
{
int start = 0;
return __get_header(req, name, &start);
}