--- 1.6.0.9/chan_sip.c 2009/04/07 07:14:33 13 +++ 1.6.0.9/chan_sip.c 2009/04/07 08:49:16 15 @@ -995,11 +995,12 @@ #define SIP_PAGE2_BUGGY_MWI (1 << 26) /*!< DP: Buggy CISCO MWI fix */ #define SIP_PAGE2_REGISTERTRYING (1 << 29) /*!< DP: Send 100 Trying on REGISTER attempts */ #define SIP_PAGE2_UDPTL_DESTINATION (1 << 30) /*!< DP: Use source IP of RTP as destination if NAT is enabled */ +#define SIP_PAGE2_Q850_REASON (1 << 31) /*!< DP: Get/send cause code via Reason header */ #define SIP_PAGE2_FLAGS_TO_COPY \ (SIP_PAGE2_ALLOWSUBSCRIBE | SIP_PAGE2_ALLOWOVERLAP | SIP_PAGE2_VIDEOSUPPORT | \ SIP_PAGE2_T38SUPPORT | SIP_PAGE2_RFC2833_COMPENSATE | SIP_PAGE2_BUGGY_MWI | \ - SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_UDPTL_DESTINATION) + SIP_PAGE2_TEXTSUPPORT | SIP_PAGE2_UDPTL_DESTINATION | SIP_PAGE2_Q850_REASON) /*@}*/ @@ -1729,12 +1730,12 @@ static int sipsock_read(int *id, int fd, short events, void *ignore); static int __sip_xmit(struct sip_pvt *p, char *data, int len); static int __sip_reliable_xmit(struct sip_pvt *p, int seqno, int resp, char *data, int len, int fatal, int sipmethod); -static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable); +static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int hangupcause); static int retrans_pkt(const void *data); static int transmit_sip_request(struct sip_pvt *p, struct sip_request *req); static int transmit_response_using_temp(ast_string_field callid, struct sockaddr_in *sin, int useglobal_nat, const int intended_method, const struct sip_request *req, const char *msg); static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req); -static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req); +static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req, int hangupcause); static int transmit_response_with_date(struct sip_pvt *p, const char *msg, const struct sip_request *req); static int transmit_response_with_sdp(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int oldsdp); static int transmit_response_with_unsupported(struct sip_pvt *p, const char *msg, const struct sip_request *req, const char *unsupported); @@ -4986,9 +4987,9 @@ } else { /* Incoming call, not up */ const char *res; if (p->hangupcause && (res = hangup_cause2sip(p->hangupcause))) - transmit_response_reliable(p, res, &p->initreq); + transmit_response_reliable(p, res, &p->initreq, p->hangupcause); else - transmit_response_reliable(p, "603 Declined", &p->initreq); + transmit_response_reliable(p, "603 Declined", &p->initreq, p->hangupcause); p->invitestate = INV_TERMINATED; } } else { /* Call is in UP state, send BYE */ @@ -5324,7 +5325,7 @@ break; case AST_CONTROL_BUSY: if (ast->_state != AST_STATE_UP) { - transmit_response_reliable(p, "486 Busy Here", &p->initreq); + transmit_response_reliable(p, "486 Busy Here", &p->initreq, 0); p->invitestate = INV_COMPLETED; sip_alreadygone(p); ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); @@ -5334,7 +5335,7 @@ break; case AST_CONTROL_CONGESTION: if (ast->_state != AST_STATE_UP) { - transmit_response_reliable(p, "503 Service Unavailable", &p->initreq); + transmit_response_reliable(p, "503 Service Unavailable", &p->initreq, 0); p->invitestate = INV_COMPLETED; sip_alreadygone(p); ast_softhangup_nolock(ast, AST_SOFTHANGUP_DEV); @@ -5404,7 +5405,7 @@ if (p->t38.state == T38_PEER_REINVITE) { AST_SCHED_DEL(sched, p->t38id); change_t38_state(p, T38_DISABLED); - transmit_response_reliable(p, "488 Not acceptable here", &p->initreq); + transmit_response_reliable(p, "488 Not acceptable here", &p->initreq, 0); } else if (p->t38.state == T38_ENABLED) transmit_reinvite_with_sdp(p, FALSE, FALSE); break; @@ -7811,7 +7812,7 @@ } /*! \brief Base transmit response function */ -static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable) +static int __transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req, enum xmittype reliable, int hangupcause) { struct sip_request resp; int seqno = 0; @@ -7824,13 +7825,26 @@ add_header_contentLength(&resp, 0); /* If we are cancelling an incoming invite for some reason, add information about the reason why we are doing this in clear text */ - if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) { + if (p->method == SIP_INVITE && msg[0] != '1') { char buf[10]; + char buf2[20]; - add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause)); - snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause); + if (!hangupcause) { + int respcode; + if (sscanf(msg, "%d ", &respcode)) + hangupcause = hangup_sip2cause(respcode); + } + + add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(hangupcause)); + snprintf(buf, sizeof(buf), "%d", hangupcause); add_header(&resp, "X-Asterisk-HangupCauseCode", buf); + + if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON)) { + sprintf(buf2, "Q.850;cause=%i", hangupcause); + add_header(&resp, "Reason", buf2); + } } + return send_response(p, &resp, reliable, seqno); } @@ -7895,7 +7909,7 @@ copy_socket_data(&p->socket, &req->socket); /* Use this temporary pvt structure to send the message */ - __transmit_response(p, msg, req, XMIT_UNRELIABLE); + __transmit_response(p, msg, req, XMIT_UNRELIABLE, 0); /* Free the string fields, but not the pool space */ ast_string_field_init(p, 0); @@ -7906,7 +7920,7 @@ /*! \brief Transmit response, no retransmits */ static int transmit_response(struct sip_pvt *p, const char *msg, const struct sip_request *req) { - return __transmit_response(p, msg, req, XMIT_UNRELIABLE); + return __transmit_response(p, msg, req, XMIT_UNRELIABLE, 0); } /*! \brief Transmit response, no retransmits */ @@ -7940,9 +7954,9 @@ /*! \brief Transmit response, Make sure you get an ACK This is only used for responses to INVITEs, where we need to make sure we get an ACK */ -static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req) +static int transmit_response_reliable(struct sip_pvt *p, const char *msg, const struct sip_request *req, int hangupcause) { - return __transmit_response(p, msg, req, req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL); + return __transmit_response(p, msg, req, req->ignore ? XMIT_UNRELIABLE : XMIT_CRITICAL, hangupcause); } /*! \brief Append date to SIP message */ @@ -9911,6 +9925,11 @@ add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->hangupcause)); snprintf(buf, sizeof(buf), "%d", p->hangupcause); add_header(&resp, "X-Asterisk-HangupCauseCode", buf); + + if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON)) { + sprintf(buf, "Q.850;cause=%i", p->hangupcause); + add_header(&resp, "Reason", buf); + } } add_header_contentLength(&resp, 0); @@ -13143,6 +13162,7 @@ ast_cli(fd, " Sess-Refresh : %s\n", strefresher2str(peer->stimer.st_ref)); ast_cli(fd, " Sess-Expires : %d secs\n", peer->stimer.st_max_se); ast_cli(fd, " Min-Sess : %d secs\n", peer->stimer.st_min_se); + ast_cli(fd, " Use Reason : %s\n", ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON) ? "Yes" : "No"); ast_cli(fd, "\n"); unref_peer(peer); } else if (peer && type == 1) { /* manager listing */ @@ -13219,6 +13239,7 @@ astman_append(s, "SIP-Useragent: %s\r\n", peer->useragent); astman_append(s, "Reg-Contact : %s\r\n", peer->fullcontact); astman_append(s, "QualifyFreq : %d ms\n", peer->qualifyfreq); + astman_append(s, "SIP-Use-Reason-Header : %s\n", (ast_test_flag(&peer->flags[1], SIP_PAGE2_Q850_REASON)) ? "Y" : "N"); if (peer->chanvars) { for (v = peer->chanvars ; v ; v = v->next) { astman_append(s, "ChanVariable:\n"); @@ -15652,9 +15673,22 @@ sipmethod = find_sip_method(msg); - owner = p->owner; - if (owner) - owner->hangupcause = hangup_sip2cause(resp); + owner = p->owner; + if (owner) { + const char *rp = NULL, *rh = NULL; + if (ast_test_flag(&p->flags[1], SIP_PAGE2_Q850_REASON) && (rh = get_header(req, "Reason"))) { + rh = ast_skip_blanks(rh); + if (!strncasecmp(rh, "Q.850", 5)) { + rp = strstr(rh, "cause="); + rp += 6; + owner->hangupcause = atoi(rp); + if (req->debug) + ast_verbose("Using Reason header for cause code:%i\n", owner->hangupcause); + } + } else { + owner->hangupcause = hangup_sip2cause(resp); + } + } /* Acknowledge whatever it is destined for */ if ((resp >= 100) && (resp <= 199)) @@ -16554,7 +16588,7 @@ if (!c) { /* What to do if no channel ??? */ ast_log(LOG_ERROR, "Unable to create new channel. Invite/replace failed.\n"); - transmit_response_reliable(p, "503 Service Unavailable", req); + transmit_response_reliable(p, "503 Service Unavailable", req, 0); append_history(p, "Xfer", "INVITE/Replace Failed. No new channel."); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); sip_pvt_unlock(p->refer->refer_call); @@ -16951,7 +16985,7 @@ struct sip_pvt *p = (struct sip_pvt *) data; change_t38_state(p, T38_DISABLED); - transmit_response_reliable(p, "488 Not acceptable here", &p->initreq); + transmit_response_reliable(p, "488 Not acceptable here", &p->initreq, 0); p->t38id = -1; return 0; } @@ -17060,7 +17094,7 @@ if (!req->ignore && p->pendinginvite) { /* We already have a pending invite. Sorry. You are on hold. */ - transmit_response_reliable(p, "491 Request Pending", req); + transmit_response_reliable(p, "491 Request Pending", req, 0); ast_debug(1, "Got INVITE on call where we already have pending INVITE, deferring that - %s\n", p->callid); /* Don't destroy dialog here */ return 0; @@ -17077,7 +17111,7 @@ if (p->owner) { ast_debug(3, "INVITE w Replaces on existing call? Refusing action. [%s]\n", p->callid); - transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */ + transmit_response_reliable(p, "400 Bad request", req, 0); /* The best way to not not accept the transfer */ /* Do not destroy existing call */ return -1; } @@ -17089,7 +17123,7 @@ ast_uri_decode(replace_id); if (!p->refer && !sip_refer_allocate(p)) { - transmit_response_reliable(p, "500 Server Internal Error", req); + transmit_response_reliable(p, "500 Server Internal Error", req, 0); append_history(p, "Xfer", "INVITE/Replace Failed. Out of memory."); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); p->invitestate = INV_COMPLETED; @@ -17127,7 +17161,7 @@ */ if ((p->refer->refer_call = get_sip_pvt_byid_locked(replace_id, totag, fromtag)) == NULL) { ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existent call id (%s)!\n", replace_id); - transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req); + transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replaces)", req, 0); error = 1; } @@ -17140,7 +17174,7 @@ if (p->refer->refer_call == p) { ast_log(LOG_NOTICE, "INVITE with replaces into it's own call id (%s == %s)!\n", replace_id, p->callid); p->refer->refer_call = dialog_unref(p->refer->refer_call); - transmit_response_reliable(p, "400 Bad request", req); /* The best way to not not accept the transfer */ + transmit_response_reliable(p, "400 Bad request", req, 0); /* The best way to not not accept the transfer */ error = 1; } @@ -17148,13 +17182,13 @@ /* Oops, someting wrong anyway, no owner, no call */ ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-existing call id (%s)!\n", replace_id); /* Check for better return code */ - transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req); + transmit_response_reliable(p, "481 Call Leg Does Not Exist (Replace)", req, 0); error = 1; } if (!error && p->refer->refer_call->owner->_state != AST_STATE_RINGING && p->refer->refer_call->owner->_state != AST_STATE_RING && p->refer->refer_call->owner->_state != AST_STATE_UP ) { ast_log(LOG_NOTICE, "Supervised transfer attempted to replace non-ringing or active call id (%s)!\n", replace_id); - transmit_response_reliable(p, "603 Declined (Replaces)", req); + transmit_response_reliable(p, "603 Declined (Replaces)", req, 0); error = 1; } @@ -17199,7 +17233,7 @@ /* Handle SDP here if we already have an owner */ if (find_sdp(req)) { if (process_sdp(p, req, SDP_T38_INITIATE)) { - transmit_response_reliable(p, "488 Not acceptable here", req); + transmit_response_reliable(p, "488 Not acceptable here", req, 0); if (!p->lastinvite) sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); return -1; @@ -17229,7 +17263,7 @@ transmit_fake_auth_response(p, SIP_INVITE, req, XMIT_RELIABLE); } else { ast_log(LOG_NOTICE, "Failed to authenticate user %s\n", get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); + transmit_response_reliable(p, "403 Forbidden", req, 0); } p->invitestate = INV_COMPLETED; sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -17241,7 +17275,7 @@ if (find_sdp(req)) { if (process_sdp(p, req, SDP_T38_INITIATE)) { /* Unacceptable codecs */ - transmit_response_reliable(p, "488 Not acceptable here", req); + transmit_response_reliable(p, "488 Not acceptable here", req, 0); p->invitestate = INV_COMPLETED; sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); ast_debug(1, "No compatible codecs for this SIP call.\n"); @@ -17268,7 +17302,7 @@ if ((res = update_call_counter(p, INC_CALL_LIMIT))) { if (res < 0) { ast_log(LOG_NOTICE, "Failed to place call for user %s, too many calls\n", p->username); - transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req); + transmit_response_reliable(p, "480 Temporarily Unavailable (Call limit) ", req, 0); sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); p->invitestate = INV_COMPLETED; } @@ -17286,11 +17320,11 @@ if (!replace_id && gotdest) { /* No matching extension found */ if (gotdest == 1 && ast_test_flag(&p->flags[1], SIP_PAGE2_ALLOWOVERLAP)) - transmit_response_reliable(p, "484 Address Incomplete", req); + transmit_response_reliable(p, "484 Address Incomplete", req, 0); else { char *decoded_exten = ast_strdupa(p->exten); - transmit_response_reliable(p, "404 Not Found", req); + transmit_response_reliable(p, "404 Not Found", req, 0); ast_uri_decode(decoded_exten); ast_log(LOG_NOTICE, "Call from '%s' to extension" " '%s' rejected because extension not found.\n", @@ -17348,7 +17382,7 @@ if (!ast_strlen_zero(p_uac_se_hdr)) { rtn = parse_session_expires(p_uac_se_hdr, &uac_max_se, &st_ref); if (rtn != 0) { - transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req); + transmit_response_reliable(p, "400 Session-Expires Invalid Syntax", req, 0); p->invitestate = INV_COMPLETED; if (!p->lastinvite) { sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -17362,7 +17396,7 @@ if (!ast_strlen_zero(p_uac_min_se)) { rtn = parse_minse(p_uac_min_se, &uac_min_se); if (rtn != 0) { - transmit_response_reliable(p, "400 Min-SE Invalid Syntax", req); + transmit_response_reliable(p, "400 Min-SE Invalid Syntax", req, 0); p->invitestate = INV_COMPLETED; if (!p->lastinvite) { sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); @@ -17490,12 +17524,12 @@ case AST_PBX_FAILED: ast_log(LOG_WARNING, "Failed to start PBX :(\n"); p->invitestate = INV_COMPLETED; - transmit_response_reliable(p, "503 Unavailable", req); + transmit_response_reliable(p, "503 Unavailable", req, 0); break; case AST_PBX_CALL_LIMIT: ast_log(LOG_WARNING, "Failed to start PBX (call limit reached) \n"); p->invitestate = INV_COMPLETED; - transmit_response_reliable(p, "480 Temporarily Unavailable", req); + transmit_response_reliable(p, "480 Temporarily Unavailable", req, 0); break; case AST_PBX_SUCCESS: /* nothing to do */ @@ -17516,7 +17550,7 @@ *nounlock = 1; if (ast_pickup_call(c)) { ast_log(LOG_NOTICE, "Nothing to pick up for %s\n", p->callid); - transmit_response_reliable(p, "503 Unavailable", req); + transmit_response_reliable(p, "503 Unavailable", req, 0); sip_alreadygone(p); /* Unlock locks so ast_hangup can do its magic */ sip_pvt_unlock(p); @@ -17571,7 +17605,7 @@ ast_log(LOG_NOTICE, "Unable to create/find SIP channel for this INVITE\n"); msg = "503 Unavailable"; } - transmit_response_reliable(p, msg, req); + transmit_response_reliable(p, msg, req, 0); p->invitestate = INV_COMPLETED; sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); } @@ -18059,7 +18093,7 @@ else sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT); if (p->initreq.len > 0) { - transmit_response_reliable(p, "487 Request Terminated", &p->initreq); + transmit_response_reliable(p, "487 Request Terminated", &p->initreq, 0); transmit_response(p, "200 OK", req); return 1; } else { @@ -18159,7 +18193,7 @@ /* If we have an INCOMING invite that we haven't answered, terminate that transaction */ if (p->pendinginvite && !ast_test_flag(&p->flags[0], SIP_OUTGOING) && !req->ignore) { - transmit_response_reliable(p, "487 Request Terminated", &p->initreq); + transmit_response_reliable(p, "487 Request Terminated", &p->initreq, 0); } __sip_pretend_ack(p); @@ -18348,7 +18382,7 @@ transmit_fake_auth_response(p, SIP_SUBSCRIBE, req, XMIT_UNRELIABLE); } else { ast_log(LOG_NOTICE, "Failed to authenticate user %s for SUBSCRIBE\n", get_header(req, "From")); - transmit_response_reliable(p, "403 Forbidden", req); + transmit_response_reliable(p, "403 Forbidden", req, 0); } p->needdestroy = 1; return 0; @@ -18748,7 +18782,7 @@ if (!p->initreq.headers && req->has_to_tag) { /* If this is a first request and it got a to-tag, it is not for us */ if (!req->ignore && req->method == SIP_INVITE) { - transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req); + transmit_response_reliable(p, "481 Call/Transaction Does Not Exist", req, 0); /* Will cease to exist after ACK */ } else if (req->method != SIP_ACK) { transmit_response(p, "481 Call/Transaction Does Not Exist", req); @@ -21096,7 +21130,9 @@ } else { peer->stimer.st_ref = i; } - } + } else if (!strcasecmp(v->name, "use_q850_reason")) { + ast_set2_flag(&peer->flags[1], ast_true(v->value), SIP_PAGE2_Q850_REASON); + } } if (!peer->socket.type) { @@ -22342,7 +22378,7 @@ } ast_string_field_build(p, our_contact, "Transfer ", extension, host, port ? ":" : "", port ? port : ""); - transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq); + transmit_response_reliable(p, "302 Moved Temporarily", &p->initreq, 0); sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Make sure we stop send this reply. */ sip_alreadygone(p);