[asterisk-commits] branch oej/metermaids-trunk r22195 -
/team/oej/metermaids-trunk/channels/
asterisk-commits at lists.digium.com
asterisk-commits at lists.digium.com
Sun Apr 23 05:55:45 MST 2006
Author: oej
Date: Sun Apr 23 07:55:44 2006
New Revision: 22195
URL: http://svn.digium.com/view/asterisk?rev=22195&view=rev
Log:
Reset automerge
Modified:
team/oej/metermaids-trunk/channels/chan_sip.c
Modified: team/oej/metermaids-trunk/channels/chan_sip.c
URL: http://svn.digium.com/view/asterisk/team/oej/metermaids-trunk/channels/chan_sip.c?rev=22195&r1=22194&r2=22195&view=diff
==============================================================================
--- team/oej/metermaids-trunk/channels/chan_sip.c (original)
+++ team/oej/metermaids-trunk/channels/chan_sip.c Sun Apr 23 07:55:44 2006
@@ -32,6 +32,8 @@
* \todo SIP over TCP
* \todo SIP over TLS
* \todo Better support of forking
+ * \todo VIA branch tag transaction checking
+ * \todo Transaction support
*
* \ingroup channel_drivers
*
@@ -59,6 +61,8 @@
* if it's a response to an outbound request, it's sent to handle_response().
* If it is a request, handle_request sends it to one of a list of functions
* depending on the request type - INVITE, OPTIONS, REFER, BYE, CANCEL etc
+ * sipsock_read locks the ast_channel if it exists (an active call) and
+ * unlocks it after we have processed the SIP message.
*
* A new INVITE is sent to handle_request_invite(), that will end up
* starting a new channel in the PBX, the new channel after that executing
@@ -187,6 +191,9 @@
#define DEFAULT_RETRANS 1000 /*!< How frequently to retransmit Default: 2 * 500 ms in RFC 3261 */
#define MAX_RETRANS 6 /*!< Try only 6 times for retransmissions, a total of 7 transmissions */
+#define SIP_TRANS_TIMEOUT 32000 /*!< SIP request timeout (rfc 3261) 64*T1
+ \todo Use known T1 for timeout (peerpoke)
+ */
#define MAX_AUTHTRIES 3 /*!< Try authentication three times, then fail */
#define SIP_MAX_HEADERS 64 /*!< Max amount of SIP headers to read */
@@ -250,7 +257,7 @@
{ CPIM_PIDF_XML, "presence", "application/cpim-pidf+xml", "cpim-pidf+xml" }, /* RFC 3863 */
{ PIDF_XML, "presence", "application/pidf+xml", "pidf+xml" }, /* RFC 3863 */
{ XPIDF_XML, "presence", "application/xpidf+xml", "xpidf+xml" }, /* Pre-RFC 3863 with MS additions */
- { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* Mailbox notification */
+ { MWI_NOTIFICATION, "message-summary", "application/simple-message-summary", "mwi" } /* RFC 3842: Mailbox notification */
};
enum sipmethod {
@@ -708,6 +715,24 @@
{ REFER_NOAUTH, "Failed - auth failure" }
} ;
+/*! \brief Structure to handle SIP transfers. Dynamically allocated when needed */
+/* OEJ: Should be moved to string fields */
+struct sip_refer {
+ char refer_to[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO extension */
+ char refer_to_domain[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO domain */
+ char refer_to_urioption[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO uri options */
+ char refer_to_context[AST_MAX_EXTENSION]; /*!< Place to store REFER-TO context */
+ char referred_by[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */
+ char referred_by_name[AST_MAX_EXTENSION]; /*!< Place to store REFERRED-BY extension */
+ char refer_contact[AST_MAX_EXTENSION]; /*!< Place to store Contact info from a REFER extension */
+ char replaces_callid[BUFSIZ]; /*!< Replace info */
+ char replaces_callid_totag[BUFSIZ/2]; /*!< Replace info */
+ char replaces_callid_fromtag[BUFSIZ/2]; /*!< Replace info */
+ struct sip_pvt *refer_call; /*!< Call we are referring */
+ int attendedtransfer; /*!< Attended or blind transfer? */
+ int localtransfer; /*!< Transfer to local domain? */
+ enum referstatus status; /*!< REFER status */
+};
/*! \brief sip_pvt: PVT structures are used for each SIP dialog, ie. a call, a registration, a subscribe */
static struct sip_pvt {
@@ -730,6 +755,7 @@
AST_STRING_FIELD(exten); /*!< Extension where to start */
AST_STRING_FIELD(context); /*!< Context for this call */
AST_STRING_FIELD(subscribecontext); /*!< Subscribecontext */
+ AST_STRING_FIELD(subscribeuri); /*!< Subscribecontext */
AST_STRING_FIELD(fromdomain); /*!< Domain to show in the from field */
AST_STRING_FIELD(fromuser); /*!< User to show in the user field */
AST_STRING_FIELD(fromname); /*!< Name to show in the user field */
@@ -801,11 +827,12 @@
int rtpholdtimeout; /*!< RTP timeout when on hold */
int rtpkeepalive; /*!< Send RTP packets for keepalive */
enum transfermodes allowtransfer; /*! SIP Refer restriction scheme */
- enum subscriptiontype subscribed; /*!< Is this dialog a subscription? */
- int stateid;
- int laststate; /*!< Last known extension state */
- int dialogver;
+ enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */
+ int stateid; /*!< SUBSCRIBE: ID for devicestate subscriptions */
+ int laststate; /*!< SUBSCRIBE: Last known extension state */
+ int dialogver; /*!< SUBSCRIBE: Version for subscription dialog-info */
+ struct sip_refer *refer; /*!< REFER: SIP transfer data structure */
struct ast_dsp *vad; /*!< Voice Activation Detection dsp */
struct sip_peer *relatedpeer; /*!< If this dialog is related to a peer, which one
@@ -815,7 +842,7 @@
struct ast_rtp *vrtp; /*!< Video RTP session */
struct sip_pkt *packets; /*!< Packets scheduled for re-transmission */
struct sip_history_head *history; /*!< History of this SIP dialog */
- struct ast_variable *chanvars; /*!< Channel variables to set for call */
+ struct ast_variable *chanvars; /*!< Channel variables to set for inbound call */
struct sip_pvt *next; /*!< Next dialog in chain */
struct sip_invite_param *options; /*!< Options for INVITE */
} *iflist = NULL;
@@ -823,9 +850,9 @@
#define FLAG_RESPONSE (1 << 0)
#define FLAG_FATAL (1 << 1)
-/*! \brief sip packet - read in sipsock_read(), transmitted in send_request() */
+/*! \brief sip packet - raw format for outbound packets that are sent or scheduled for transmission */
struct sip_pkt {
- struct sip_pkt *next; /*!< Next packet */
+ struct sip_pkt *next; /*!< Next packet in linked list */
int retrans; /*!< Retransmission number */
int method; /*!< SIP method for this packet */
int seqno; /*!< Sequence number */
@@ -1079,6 +1106,11 @@
static void handle_request_info(struct sip_pvt *p, struct sip_request *req);
static int handle_request_options(struct sip_pvt *p, struct sip_request *req);
+/*------Response handling functions */
+static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno);
+static int handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req);
+
/*----- RTP interface functions */
static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struct ast_rtp *vrtp, int codecs, int nat_active);
static struct ast_rtp *sip_get_rtp_peer(struct ast_channel *chan);
@@ -1129,6 +1161,22 @@
return "";
}
+/*! \brief Initialize the initital request packet in the pvt structure.
+ This packet is used for creating replies and future requests in
+ a dialog */
+void initialize_initreq(struct sip_pvt *p, struct sip_request *req)
+{
+ if (p->initreq.headers) {
+ ast_log(LOG_WARNING, "Initializing already initialized SIP dialog??? %s\n", p->callid);
+ return;
+ }
+ /* Use this as the basis */
+ copy_request(&p->initreq, req);
+ parse_request(&p->initreq);
+ if (ast_test_flag(req, SIP_PKT_DEBUG))
+ ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+}
+
/*! \brief returns true if 'name' (with optional trailing whitespace)
* matches the sip method 'id'.
@@ -1339,11 +1387,11 @@
/*! \brief Retransmit SIP message if no answer */
static int retrans_pkt(void *data)
{
- struct sip_pkt *pkt=data, *prev, *cur = NULL;
+ struct sip_pkt *pkt = data, *prev, *cur = NULL;
char iabuf[INET_ADDRSTRLEN];
int reschedule = DEFAULT_RETRANS;
- /* Lock channel */
+ /* Lock channel PVT */
ast_mutex_lock(&pkt->owner->lock);
if (pkt->retrans < MAX_RETRANS) {
@@ -1406,7 +1454,7 @@
ast_set_flag(&pkt->owner->flags[0], SIP_ALREADYGONE);
ast_log(LOG_WARNING, "Hanging up call %s - no reply to our critical packet.\n", pkt->owner->callid);
ast_queue_hangup(pkt->owner->owner);
- ast_mutex_unlock(&pkt->owner->owner->lock);
+ ast_channel_unlock(pkt->owner->owner);
} else {
/* If no channel owner, destroy now */
ast_set_flag(&pkt->owner->flags[0], SIP_NEEDDESTROY);
@@ -2153,17 +2201,15 @@
p->initid = -1;
if (p->owner) {
/* XXX fails on possible deadlock */
- if (!ast_mutex_trylock(&p->owner->lock)) {
+ if (!ast_channel_trylock(p->owner)) {
ast_log(LOG_NOTICE, "Auto-congesting %s\n", p->owner->name);
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
- ast_mutex_unlock(&p->owner->lock);
+ ast_channel_unlock(p->owner);
}
}
ast_mutex_unlock(&p->lock);
return 0;
}
-
-
/*! \brief Initiate SIP call from PBX
@@ -2174,7 +2220,8 @@
struct sip_pvt *p;
struct varshead *headp;
struct ast_var_t *current;
-
+ const char *referer = NULL; /* SIP refererer */
+
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);
@@ -2195,13 +2242,36 @@
} 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 */
p->options->addsipheaders = 1;
+ } else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER")) {
+ /* This is a transfered call */
+ p->options->transfer = 1;
+ } else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER_REFERER")) {
+ /* This is the referer */
+ referer = ast_var_value(current);
+ } else if (!strcasecmp(ast_var_name(current),"SIPTRANSFER_REPLACES")) {
+ /* We're replacing a call. */
+ p->options->replaces = ast_var_value(current);
}
}
res = 0;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
+
+ if (p->options->transfer) {
+ char buf[BUFSIZ/2];
+
+ if (referer) {
+ if (sipdebug && option_debug > 2)
+ ast_log(LOG_DEBUG, "Call for %s transfered by %s\n", p->username, referer);
+ snprintf(buf, sizeof(buf)-1, "-> %s (via %s)", p->cid_name, referer);
+ } else {
+ snprintf(buf, sizeof(buf)-1, "-> %s", p->cid_name);
+ }
+ ast_string_field_set(p, cid_name, buf);
+ }
if (option_debug)
ast_log(LOG_DEBUG, "Outgoing Call for %s\n", p->username);
+
res = update_call_counter(p, INC_CALL_LIMIT);
if ( res != -1 ) {
p->callingpres = ast->cid.cid_pres;
@@ -2210,6 +2280,8 @@
if (p->maxtime) {
/* Initialize auto-congest time */
p->initid = ast_sched_add(sched, p->maxtime * 4, auto_congest, p);
+ } else {
+ p->initid = ast_sched_add(sched, SIP_TRANS_TIMEOUT, auto_congest, p);
}
}
return res;
@@ -2271,6 +2343,8 @@
ast_rtp_destroy(p->rtp);
if (p->vrtp)
ast_rtp_destroy(p->vrtp);
+ if (p->refer)
+ free(p->refer);
if (p->route) {
free_old_route(p->route);
p->route = NULL;
@@ -2284,12 +2358,12 @@
/* Unlink us from the owner if we have one */
if (p->owner) {
if (lockowner)
- ast_mutex_lock(&p->owner->lock);
+ ast_channel_lock(p->owner);
if (option_debug)
ast_log(LOG_DEBUG, "Detaching from %s\n", p->owner->name);
p->owner->tech_pvt = NULL;
if (lockowner)
- ast_mutex_unlock(&p->owner->lock);
+ ast_channel_unlock(p->owner);
}
/* Clear history */
if (p->history) {
@@ -2654,7 +2728,7 @@
INVITE, but do set an autodestruct just in case we never get it. */
ast_clear_flag(&locflags, SIP_NEEDDESTROY);
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
if ( p->initid != -1 ) {
/* channel still up - reverse dec of inUse counter
only if the channel is not auto-congested */
@@ -2721,7 +2795,7 @@
ast_setstate(ast, AST_STATE_UP);
if (option_debug)
ast_log(LOG_DEBUG, "SIP answering channel: %s\n", ast->name);
- res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_RELIABLE);
+ res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
}
ast_mutex_unlock(&p->lock);
return res;
@@ -2798,11 +2872,12 @@
p = newchan->tech_pvt;
ast_mutex_lock(&p->lock);
+ append_history(p, "Masq", "Old channel: %s\n", oldchan->name);
+ append_history(p, "Masq (cont)", "...new owner: %s\n", p->owner->name);
if (p->owner != oldchan)
ast_log(LOG_WARNING, "old channel wasn't %p but was %p\n", oldchan, p->owner);
else {
p->owner = newchan;
- append_history(p, "Masq", "Old channel: %s\n", oldchan->name);
ret = 0;
}
ast_mutex_unlock(&p->lock);
@@ -3048,7 +3123,8 @@
for (v = i->chanvars ; v ; v = v->next)
pbx_builtin_setvar_helper(tmp,v->name,v->value);
- append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
+ if (recordhistory)
+ append_history(i, "NewChan", "Channel %s - from %s", tmp->name, i->callid);
return tmp;
}
@@ -3380,6 +3456,12 @@
char totag[128];
char fromtag[128];
const char *callid = get_header(req, "Call-ID");
+ const char *from = get_header(req, "From");
+ const char *to = get_header(req, "To");
+ const char *cseq = get_header(req, "Cseq");
+
+ if (!callid || !to || !from || !cseq) /* Call-ID, to, from and Cseq are required by RFC 3261. (Max-forwards and via too - ignored now) */
+ return NULL; /* Invalid packet */
if (pedanticsipchecking) {
/* In principle Call-ID's uniquely identify a call, but with a forking SIP proxy
@@ -3562,7 +3644,9 @@
return t;
}
-/*! \brief Parse a SIP message */
+/*! \brief Parse a SIP message
+ \note this function is used both on incoming and outgoing packets
+*/
static void parse_request(struct sip_request *req)
{
/* Divide fields by NULL's */
@@ -4244,6 +4328,7 @@
copy_header(resp, req, "CSeq");
add_header(resp, "User-Agent", global_useragent);
add_header(resp, "Allow", ALLOWED_METHODS);
+ add_header(resp, "Supported", SUPPORTED_EXTENSIONS);
if (msg[0] == '2' && (p->method == SIP_SUBSCRIBE || p->method == SIP_REGISTER)) {
/* For registration responses, we also need expiry and
contact info */
@@ -4378,8 +4463,12 @@
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 (msg[0] != '1' && p->owner && p->owner->hangupcause) {
+ if (p->method == SIP_INVITE && msg[0] != '1' && p->owner && p->owner->hangupcause) {
+ char buf[10];
+
add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
+ snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
+ add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
}
add_blank_header(&resp);
return send_response(p, &resp, reliable, seqno);
@@ -4846,10 +4935,8 @@
static int transmit_reinvite_with_sdp(struct sip_pvt *p)
{
struct sip_request req;
- if (ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE))
- reqprep(&req, p, SIP_UPDATE, 0, 1);
- else
- reqprep(&req, p, SIP_INVITE, 0, 1);
+
+ reqprep(&req, p, ast_test_flag(&p->flags[0], SIP_REINVITE_UPDATE) ? SIP_UPDATE : SIP_INVITE, 0, 1);
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
@@ -4859,10 +4946,7 @@
append_history(p, "ReInv", "Re-invite sent");
add_sdp(&req, p);
/* Use this as the basis */
- copy_request(&p->initreq, &req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p))
- ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
+ initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
ast_set_flag(&p->flags[0], SIP_OUTGOING);
return send_request(p, &req, 1, p->ocseq);
@@ -5139,13 +5223,24 @@
add_header(&req, p->options->authheader, p->options->auth);
append_date(&req);
if (sipmethod == SIP_REFER) { /* Call transfer */
- if (!ast_strlen_zero(p->refer_to))
- add_header(&req, "Refer-To", p->refer_to);
- if (!ast_strlen_zero(p->referred_by))
- add_header(&req, "Referred-By", p->referred_by);
- }
- if (p->options && !ast_strlen_zero(p->options->distinctive_ring))
- {
+ if (p->refer) {
+ char buf[BUFSIZ];
+ if (!ast_strlen_zero(p->refer->refer_to))
+ add_header(&req, "Refer-To", p->refer->refer_to);
+ if (!ast_strlen_zero(p->refer->referred_by)) {
+ sprintf(buf, "%s <%s>", p->refer->referred_by_name, p->refer->referred_by);
+ add_header(&req, "Referred-By", buf);
+ }
+ }
+ }
+ /* This new INVITE is part of an attended transfer. Make sure that the
+ other end knows and replace the current call with this new call */
+ if (p->options && p->options->replaces && !ast_strlen_zero(p->options->replaces)) {
+ add_header(&req, "Replaces", p->options->replaces);
+ add_header(&req, "Required", "replaces");
+ }
+
+ if (p->options && !ast_strlen_zero(p->options->distinctive_ring)) {
add_header(&req, "Alert-Info", p->options->distinctive_ring);
}
add_header(&req, "Allow", ALLOWED_METHODS);
@@ -5196,13 +5291,8 @@
add_blank_header(&req);
}
- if (!p->initreq.headers) {
- /* Use this as the basis */
- copy_request(&p->initreq, &req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p))
- ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- }
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
p->lastinvite = p->ocseq;
return send_request(p, &req, init ? 2 : 1, p->ocseq);
}
@@ -5405,42 +5495,30 @@
add_header_contentLength(&req, strlen(tmp));
add_line(&req, tmp);
- if (!p->initreq.headers) { /* Use this as the basis */
- copy_request(&p->initreq, &req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p))
- ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- determine_firstline_parts(&p->initreq);
- }
-
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
/*! \brief Transmit SIP request */
static int transmit_sip_request(struct sip_pvt *p,struct sip_request *req)
{
- if (!p->initreq.headers) {
- /* Use this as the basis */
- copy_request(&p->initreq, req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p))
- ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- determine_firstline_parts(&p->initreq);
- }
-
+ if (!p->initreq.headers)
+ initialize_initreq(p, req);
return send_request(p, req, 0, p->ocseq);
}
-/*! \brief Notify a transferring party of the status of transfer
- */
-static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message)
+/*! \brief Notify a transferring party of the status of transfer */
+static int transmit_notify_with_sipfrag(struct sip_pvt *p, int cseq, char *message, int terminate)
{
struct sip_request req;
- char tmp[50];
+ char tmp[BUFSIZ/2];
+
reqprep(&req, p, SIP_NOTIFY, 0, 1);
snprintf(tmp, sizeof(tmp), "refer;id=%d", cseq);
add_header(&req, "Event", tmp);
- add_header(&req, "Subscription-state", "terminated;reason=noresource");
+ if (terminate)
+ add_header(&req, "Subscription-state", "terminated;reason=noresource");
add_header(&req, "Content-Type", "message/sipfrag;version=2.0");
add_header(&req, "Allow", ALLOWED_METHODS);
add_header(&req, "Supported", SUPPORTED_EXTENSIONS);
@@ -5449,14 +5527,14 @@
add_header_contentLength(&req, strlen(tmp));
add_line(&req, tmp);
- if (!p->initreq.headers) {
- /* Use this as the basis */
- copy_request(&p->initreq, &req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p))
- ast_verbose("%d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- determine_firstline_parts(&p->initreq);
- }
+
+ snprintf(tmp, sizeof(tmp), "SIP/2.0 %s\r\n", message);
+ add_header_contentLength(&req, strlen(tmp));
+ add_line(&req, tmp);
+
+
+ if (!p->initreq.headers)
+ initialize_initreq(p, &req);
return send_request(p, &req, 1, p->ocseq);
}
@@ -5732,12 +5810,10 @@
add_header(&req, "Event", "registration");
add_header_contentLength(&req, 0);
add_blank_header(&req);
- copy_request(&p->initreq, &req);
- parse_request(&p->initreq);
- if (sip_debug_test_pvt(p)) {
+
+ initialize_initreq(p, &req);
+ if (sip_debug_test_pvt(p))
ast_verbose("REGISTER %d headers, %d lines\n", p->initreq.headers, p->initreq.lines);
- }
- determine_firstline_parts(&p->initreq);
r->regstate = auth ? REG_STATE_AUTHSENT : REG_STATE_REGSENT;
r->regattempts++; /* Another attempt */
if (option_debug > 3)
@@ -5755,6 +5831,12 @@
return send_request(p, &req, 1, p->ocseq);
}
+/*! \brief Allocate SIP refer structure */
+int sip_refer_allocate(struct sip_pvt *p) {
+ p->refer = ast_calloc(1, sizeof(struct sip_refer));
+ return p->refer ? 1 : 0;
+}
+
/*! \brief Transmit SIP REFER message */
static int transmit_refer(struct sip_pvt *p, const char *dest)
{
@@ -5763,12 +5845,23 @@
const char *of;
char *c;
char referto[256];
-
- /* Are we transfering an inbound or outbound call? */
- if (ast_test_flag(&p->flags[0], SIP_OUTGOING))
+ char *ttag, *ftag;
+ char *theirtag = ast_strdupa(p->theirtag);
+
+ if (option_debug || sipdebug)
+ ast_log(LOG_DEBUG, "SIP transfer of %s to %s\n", p->callid, dest);
+
+ /* Are we transfering an inbound or outbound call ? */
+ if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
of = get_header(&p->initreq, "To");
- else
+ ttag = theirtag;
+ ftag = p->tag;
+ } else {
of = get_header(&p->initreq, "From");
+ ftag = theirtag;
+ ttag = p->tag;
+ }
+
ast_copy_string(from, of, sizeof(from));
of = get_in_brackets(from);
ast_string_field_set(p, from, of);
@@ -5781,17 +5874,18 @@
c = NULL;
else if ((c = strchr(of, '@')))
*c++ = '\0';
- if (c) {
+ if (c)
snprintf(referto, sizeof(referto), "<sip:%s@%s>", dest, c);
- } else {
+ else
snprintf(referto, sizeof(referto), "<sip:%s>", dest);
- }
add_header(&req, "Max-Forwards", DEFAULT_MAX_FORWARDS);
/* save in case we get 407 challenge */
- ast_string_field_set(p, refer_to, referto);
- ast_string_field_set(p, referred_by, p->our_contact);
+ sip_refer_allocate(p);
+ ast_copy_string(p->refer->refer_to, referto, sizeof(p->refer->refer_to));
+ ast_copy_string(p->refer->referred_by, p->our_contact, sizeof(p->refer->referred_by));
+ p->refer->status = REFER_SENT; /* Set refer status */
reqprep(&req, p, SIP_REFER, 0, 1);
add_header(&req, "Refer-To", referto);
@@ -5800,7 +5894,10 @@
if (!ast_strlen_zero(p->our_contact))
add_header(&req, "Referred-By", p->our_contact);
add_blank_header(&req);
+
return send_request(p, &req, 1, p->ocseq);
+ /* We should propably wait for a NOTIFY here until we ack the transfer */
+ /* Maybe fork a new thread and wait for a STATUS of REFER_200OK on the refer status before returning to app_transfer */
/*! \todo In theory, we should hang around and wait for a reply, before
returning to the dial plan here. Don't know really how that would
@@ -5808,6 +5905,7 @@
useful we should have a STATUS code on transfer().
*/
}
+
/*! \brief Send SIP INFO dtmf message, see Cisco documentation on cisco.com */
static int transmit_info_with_digit(struct sip_pvt *p, char digit)
@@ -5859,10 +5957,12 @@
}
/* If we are hanging up and know a cause for that, send it in clear text to make
debugging easier. */
- if (sipmethod == SIP_BYE) {
- if (p->owner && p->owner->hangupcause) {
- add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
- }
+ if (sipmethod == SIP_BYE && p->owner && p->owner->hangupcause) {
+ char buf[10];
+
+ add_header(&resp, "X-Asterisk-HangupCause", ast_cause2str(p->owner->hangupcause));
+ snprintf(buf, sizeof(buf), "%d", p->owner->hangupcause);
+ add_header(&resp, "X-Asterisk-HangupCauseCode", buf);
}
add_header_contentLength(&resp, 0);
@@ -6375,7 +6475,7 @@
retransmission should get it */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
/* Schedule auto destroy in 32 seconds (according to RFC 3261) */
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
}
return 1; /* Auth sent */
} else if (ast_strlen_zero(p->randdata) || ast_strlen_zero(authtoken)) {
@@ -6383,7 +6483,7 @@
ast_string_field_build(p, randdata, "%08lx", ast_random()); /* Create nonce for challenge */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
/* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
return 1; /* Auth sent */
} else { /* We have auth, so check it */
/* Whoever came up with the authentication section of SIP can suck my %&#$&* for not putting
@@ -6484,7 +6584,7 @@
}
/* Schedule auto destroy in 32 seconds */
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
return 1; /* XXX should it be -1 ? */
}
if (good_response) /* Auth is OK */
@@ -6494,7 +6594,7 @@
/* Ok, we have a bad username/secret pair */
/* Challenge again, and again, and again */
transmit_response_with_auth(p, response, req, p->randdata, reliable, respheader, 0);
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
return 1; /* Challenge sent */
}
@@ -6512,7 +6612,7 @@
case AST_EXTENSION_REMOVED: /* Extension is gone */
if (p->autokillid > -1)
sip_cancel_destroy(p); /* Remove subscription expiry for renewals */
- sip_scheddestroy(p, 15000); /* Delete subscription in 15 secs */
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT); /* Delete subscription in 32 secs */
ast_verbose(VERBOSE_PREFIX_2 "Extension state: Watcher for hint %s %s. Notify User %s\n", exten, state == AST_EXTENSION_DEACTIVATED ? "deactivated" : "removed", p->username);
p->stateid = -1;
p->subscribed = NONE;
@@ -6807,19 +6907,46 @@
return -1;
}
-/*! \brief Lock interface lock and find matching pvt lock */
-static struct sip_pvt *get_sip_pvt_byid_locked(char *callid)
+/*! \brief Lock interface lock and find matching pvt lock
+ - Their tag is fromtag, our tag is to-tag
+ - This means that in some transactions, totag needs to be their tag :-)
+ depending upon the direction
+*/
+static struct sip_pvt *get_sip_pvt_byid_locked(char *callid, char *totag, char *fromtag)
{
struct sip_pvt *sip_pvt_ptr = NULL;
/* Search interfaces and find the match */
ast_mutex_lock(&iflock);
- for (sip_pvt_ptr = iflist; sip_pvt_ptr ; sip_pvt_ptr = sip_pvt_ptr->next) {
+
+ if (option_debug > 3 && totag)
+ ast_log(LOG_DEBUG, "Looking for callid %s (fromtag %s totag %s)\n", callid, fromtag ? fromtag : "<no fromtag>", totag ? totag : "<no totag>");
+
+ for (sip_pvt_ptr = iflist; sip_pvt_ptr; sip_pvt_ptr = sip_pvt_ptr->next) {
if (!strcmp(sip_pvt_ptr->callid, callid)) {
+ int match = 1;
+ char *ourtag = sip_pvt_ptr->tag;
+
/* Go ahead and lock it (and its owner) before returning */
ast_mutex_lock(&sip_pvt_ptr->lock);
+
+ /* Check if tags match. If not, this is not the call we want
+ (With a forking SIP proxy, several call legs share the
+ call id, but have different tags)
+ */
+ if (pedanticsipchecking && (strcmp(fromtag, sip_pvt_ptr->theirtag) || strcmp(totag, ourtag)))
+ match = 0;
+
+ if (!match) {
+ ast_mutex_unlock(&sip_pvt_ptr->lock);
+ break;
+ }
+
+ if (option_debug > 3 && totag)
+ ast_log(LOG_DEBUG, "Matched %s call - their tag is %s Our tag is %s\n", ast_test_flag(&sip_pvt_ptr->flags[0], SIP_OUTGOING) ? "OUTGOING": "INCOMING", sip_pvt_ptr->theirtag, sip_pvt_ptr->tag);
+
if (sip_pvt_ptr->owner) {
- while(ast_mutex_trylock(&sip_pvt_ptr->owner->lock)) {
+ while(ast_channel_trylock(sip_pvt_ptr->owner)) {
ast_mutex_unlock(&sip_pvt_ptr->lock);
usleep(1);
ast_mutex_lock(&sip_pvt_ptr->lock);
@@ -6831,6 +6958,8 @@
}
}
ast_mutex_unlock(&iflock);
+ if (option_debug > 3 && !sip_pvt_ptr)
+ ast_log(LOG_DEBUG, "Found no match for callid %s to-tag %s from-tag %s\n", callid, totag, fromtag);
return sip_pvt_ptr;
}
@@ -6921,13 +7050,13 @@
}
if (!ast_strlen_zero(replace_callid)) {
/* This is a supervised transfer */
- ast_log(LOG_DEBUG,"Assigning Replace-Call-ID Info %s to REPLACE_CALL_ID\n",replace_callid);
+ ast_log(LOG_DEBUG,"Assigning Replace-Call-ID Info %s to REPLACE_CALL_ID\n", replace_callid);
ast_string_field_free(sip_pvt, refer_to);
ast_string_field_free(sip_pvt, referred_by);
ast_string_field_free(sip_pvt, refer_contact);
sip_pvt->refer_call = NULL;
- if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid))) {
+ if ((sip_pvt_ptr = get_sip_pvt_byid_locked(replace_callid, NULL, NULL))) {
sip_pvt->refer_call = sip_pvt_ptr;
if (sip_pvt->refer_call == sip_pvt) {
ast_log(LOG_NOTICE, "Supervised transfer attempted to transfer into same call id (%s == %s)!\n", replace_callid, sip_pvt->callid);
@@ -8563,10 +8692,12 @@
{
#define FORMAT3 "%-15.15s %-10.10s %-11.11s %-15.15s %-13.13s %-15.15s %-10.10s\n"
#define FORMAT2 "%-15.15s %-10.10s %-11.11s %-11.11s %-4.4s %-7.7s %-15.15s\n"
-#define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-4.4s %-3.3s %-3.3s %-15.15s\n"
+#define FORMAT "%-15.15s %-10.10s %-11.11s %5.5d/%5.5d %-4.4s %-3.3s %-3.3s %-15.15s %-10.10s\n"
struct sip_pvt *cur;
char iabuf[INET_ADDRSTRLEN];
int numchans = 0;
+ char *referstatus = NULL;
+
if (argc != 3)
return RESULT_SHOWUSAGE;
ast_mutex_lock(&iflock);
@@ -8576,6 +8707,10 @@
else
ast_cli(fd, FORMAT3, "Peer", "User", "Call ID", "Extension", "Last state", "Type", "Mailbox");
for (; cur; cur = cur->next) {
+ referstatus = "";
+ if (cur->refer) { /* SIP transfer in progress */
+ referstatus = referstatus2str(cur->refer->status);
+ }
if (cur->subscribed == NONE && !subscriptions) {
ast_cli(fd, FORMAT, ast_inet_ntoa(iabuf, sizeof(iabuf), cur->sa.sin_addr),
S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
@@ -8584,7 +8719,9 @@
ast_getformatname(cur->owner ? cur->owner->nativeformats : 0),
ast_test_flag(&cur->flags[0], SIP_CALL_ONHOLD) ? "Yes" : "No",
ast_test_flag(&cur->flags[0], SIP_NEEDDESTROY) ? "(d)" : "",
- cur->lastmsg );
+ cur->lastmsg ,
+ referstatus
+ );
numchans++;
}
if (cur->subscribed != NONE && subscriptions) {
@@ -8592,7 +8729,7 @@
S_OR(cur->username, S_OR(cur->cid_num, "(None)")),
cur->callid,
/* the 'complete' exten/context is hidden in the refer_to field for subscriptions */
- cur->subscribed == MWI_NOTIFICATION ? "--" : cur->refer_to,
+ cur->subscribed == MWI_NOTIFICATION ? "--" : cur->subscribeuri,
cur->subscribed == MWI_NOTIFICATION ? "<none>" : ast_extension_state2str(cur->laststate),
subscription_type2str(cur->subscribed),
cur->subscribed == MWI_NOTIFICATION ? (cur->relatedpeer ? cur->relatedpeer->mailbox : "<none>") : "<none>"
@@ -9088,7 +9225,7 @@
build_callid_pvt(p);
ast_cli(fd, "Sending NOTIFY of type '%s' to '%s'\n", argv[2], argv[i]);
transmit_sip_request(p, &req);
- sip_scheddestroy(p, 15000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
}
return RESULT_SUCCESS;
@@ -9423,10 +9560,10 @@
return -1;
}
- ast_mutex_lock(&chan->lock);
+ ast_channel_lock(chan);
if (chan->tech != &sip_tech) {
ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
@@ -9434,19 +9571,19 @@
/* If there is no private structure, this channel is no longer alive */
if (!p) {
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
content = get_header(&p->initreq, data);
if (ast_strlen_zero(content)) {
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
ast_copy_string(buf, content, len);
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return 0;
}
@@ -9589,10 +9726,10 @@
return -1;
}
- ast_mutex_lock(&chan->lock);
+ ast_channel_lock(chan);
if (chan->tech != &sip_tech) {
ast_log(LOG_WARNING, "This function can only be used on SIP channels.\n");
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
@@ -9600,7 +9737,7 @@
/* If there is no private structure, this channel is no longer alive */
if (!p) {
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
@@ -9617,10 +9754,10 @@
} else if (!strcasecmp(data, "peername")) {
ast_copy_string(buf, p->peername, len);
} else {
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return -1;
}
- ast_mutex_unlock(&chan->lock);
+ ast_channel_unlock(chan);
return 0;
}
@@ -9703,7 +9840,7 @@
}
/*! \brief Handle SIP response in dialogue */
-static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno)
+static void handle_response_invite(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
{
int outgoing = ast_test_flag(&p->flags[0], SIP_OUTGOING);
@@ -9720,41 +9857,48 @@
return;
}
+ /* Acknowledge sequence number - This only happens on INVITE from SIP-call */
+ if (p->initid > -1) {
+ /* Don't auto congest anymore since we've gotten something useful back */
+ ast_sched_del(sched, p->initid);
+ p->initid = -1;
+ }
+
switch (resp) {
case 100: /* Trying */
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
sip_cancel_destroy(p);
break;
case 180: /* 180 Ringing */
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
sip_cancel_destroy(p);
- if (!ignore && p->owner) {
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
ast_queue_control(p->owner, AST_CONTROL_RINGING);
if (p->owner->_state != AST_STATE_UP)
ast_setstate(p->owner, AST_STATE_RINGING);
}
if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
process_sdp(p, req);
- if (!ignore && p->owner) {
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
/* Queue a progress frame only if we have SDP in 180 */
ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
}
}
break;
case 183: /* Session progress */
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
sip_cancel_destroy(p);
/* Ignore 183 Session progress without SDP */
if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
process_sdp(p, req);
- if (!ignore && p->owner) {
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
/* Queue a progress frame */
ast_queue_control(p->owner, AST_CONTROL_PROGRESS);
}
}
break;
case 200: /* 200 OK on invite - someone's answering our call */
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
sip_cancel_destroy(p);
p->authtries = 0;
if (!strcasecmp(get_header(req, "Content-Type"), "application/sdp"))
@@ -9773,7 +9917,7 @@
should we care about resolving the contact
or should we just send it?
*/
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
}
@@ -9781,7 +9925,7 @@
build_route(p, req, 1);
}
- if (!ignore && p->owner) {
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner) {
if (p->owner->_state != AST_STATE_UP) {
ast_queue_control(p->owner, AST_CONTROL_ANSWER);
} else { /* RE-invite */
@@ -9791,7 +9935,7 @@
/* It's possible we're getting an 200 OK after we've tried to disconnect
by sending CANCEL */
/* First send ACK, then send bye */
- if (!ignore)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE))
ast_set_flag(&p->flags[0], SIP_PENDINGBYE);
}
/* If I understand this right, the branch is different for a non-200 ACK only */
@@ -9807,7 +9951,7 @@
/* Then we AUTH */
ast_string_field_free(p, theirtag); /* forget their old tag, so we don't match tags when getting response */
- if (!ignore) {
+ if (!ast_test_flag(req, SIP_PKT_IGNORE)) {
char *authenticate = (resp == 401 ? "WWW-Authenticate" : "Proxy-Authenticate");
char *authorization = (resp == 401 ? "Authorization" : "Proxy-Authorization");
if ((p->authtries == MAX_AUTHTRIES) || do_proxy_auth(p, req, authenticate, authorization, SIP_INVITE, 1)) {
@@ -9823,14 +9967,14 @@
/* First we ACK */
transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, 0);
ast_log(LOG_WARNING, "Received response: \"Forbidden\" from '%s'\n", get_header(&p->initreq, "From"));
- if (!ignore && p->owner)
+ if (!ast_test_flag(req, SIP_PKT_IGNORE) && p->owner)
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
break;
case 404: /* Not found */
transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, 0);
- if (p->owner && !ignore)
+ if (p->owner && !ast_test_flag(req, SIP_PKT_IGNORE))
ast_queue_control(p->owner, AST_CONTROL_CONGESTION);
ast_set_flag(&p->flags[0], SIP_ALREADYGONE);
break;
@@ -9854,7 +9998,7 @@
/* \brief Handle SIP response in REFER transaction
We've sent a REFER, now handle responses to it
*/
-static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno)
+static void handle_response_refer(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int seqno)
{
char *auth = "Proxy-Authenticate";
char *auth2 = "Proxy-Authorization";
@@ -9966,7 +10110,7 @@
r->call = NULL;
p->registry = NULL;
/* Let this one hang around until we have all the responses */
- sip_scheddestroy(p, 32000);
+ sip_scheddestroy(p, SIP_TRANS_TIMEOUT);
/* ast_set_flag(&p->flags[0], SIP_NEEDDESTROY); */
/* set us up for re-registering */
@@ -10021,7 +10165,7 @@
}
/*! \brief Handle qualification responses (OPTIONS) */
-static int handle_response_peerpoke(struct sip_pvt *p, int resp, char *rest, struct sip_request *req, int ignore, int seqno, int sipmethod)
+static int handle_response_peerpoke(struct sip_pvt *p, int resp, struct sip_request *req)
{
struct sip_peer *peer;
int pingtime;
@@ -10063,8 +10207,6 @@
if (peer->pokeexpire > -1)
ast_sched_del(sched, peer->pokeexpire);
- if (sipmethod == SIP_INVITE) /* Does this really happen? */
- transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, 0);
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
/* Try again eventually */
@@ -10115,32 +10257,28 @@
Well, as long as it's not a 100 response... since we might
need to hang around for something more "definitive" */
- res = handle_response_peerpoke(p, resp, rest, req, ignore, seqno, sipmethod);
+ res = handle_response_peerpoke(p, resp, req);
} else if (ast_test_flag(&p->flags[0], SIP_OUTGOING)) {
- /* Acknowledge sequence number */
- if (p->initid > -1) {
- /* Don't auto congest anymore since we've gotten something useful back */
- ast_sched_del(sched, p->initid);
- p->initid = -1;
- }
switch(resp) {
case 100: /* 100 Trying */
if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, ignore, seqno);
+ handle_response_invite(p, resp, rest, req, seqno);
break;
case 183: /* 183 Session Progress */
if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, ignore, seqno);
+ handle_response_invite(p, resp, rest, req, seqno);
break;
case 180: /* 180 Ringing */
if (sipmethod == SIP_INVITE)
- handle_response_invite(p, resp, rest, req, ignore, seqno);
+ handle_response_invite(p, resp, rest, req, seqno);
break;
case 200: /* 200 OK */
p->authtries = 0; /* Reset authentication counter */
if (sipmethod == SIP_MESSAGE) {
/* We successfully transmitted a message */
ast_set_flag(&p->flags[0], SIP_NEEDDESTROY);
+ } else if (sipmethod == SIP_INVITE) {
[... 662 lines stripped ...]
More information about the asterisk-commits
mailing list