Merged revisions 219303 via svnmerge from
[asterisk/asterisk.git] / channels / chan_sip.c
index ef06268..e1081c4 100644 (file)
@@ -2685,7 +2685,7 @@ static int handle_request_message(struct sip_pvt *p, struct sip_request *req);
 static int handle_request_subscribe(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e);
 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);
-static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin);
+static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *nounlock);
 static int handle_request_notify(struct sip_pvt *p, struct sip_request *req, struct sockaddr_in *sin, int seqno, const char *e);
 static int local_attended_transfer(struct sip_pvt *transferer, struct sip_dual *current, struct sip_request *req, int seqno);
 
@@ -19632,10 +19632,13 @@ static int handle_request_options(struct sip_pvt *p, struct sip_request *req)
     meaning a target pickup or an attended transfer.
     Used only once.
        XXX 'ignore' is unused.
+
+       \note this function is called by handle_request_invite(). Four locks
+       held at the beginning of this function, p, p->owner, p->refer->refer_call->owner...
+       only p's lock should remain at the end of this function.  p's lock is held by sipsock_read()
  */
-static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin)
+static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, int debug, int seqno, struct sockaddr_in *sin, int *nounlock)
 {
-       struct ast_frame *f;
        int earlyreplace = 0;
        int oneleggedreplace = 0;               /* Call with no bridge, propably IVR or voice message */
        struct ast_channel *c = p->owner;       /* Our incoming call */
@@ -19672,6 +19675,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in
                transmit_response_with_sdp(p, "200 OK", req, XMIT_RELIABLE, FALSE, FALSE);
                /* Do something more clever here */
                ast_channel_unlock(c);
+               ast_channel_unlock(replacecall);
                sip_pvt_unlock(p->refer->refer_call);
                return 1;
        }
@@ -19681,6 +19685,7 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in
                transmit_response_reliable(p, "503 Service Unavailable", req);
                append_history(p, "Xfer", "INVITE/Replace Failed. No new channel.");
                sip_scheddestroy(p, DEFAULT_TRANS_TIMEOUT);
+               ast_channel_unlock(replacecall);
                sip_pvt_unlock(p->refer->refer_call);
                return 1;
        }
@@ -19711,53 +19716,25 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in
        ast_quiet_chan(replacecall);
        ast_quiet_chan(targetcall);
        ast_debug(4, "Invite/Replaces: preparing to masquerade %s into %s\n", c->name, replacecall->name);
-       /* Unlock clone, but not original (replacecall) */
-       if (!oneleggedreplace)
-               ast_channel_unlock(c);
-
-       /* Unlock PVT */
-       sip_pvt_unlock(p->refer->refer_call);
 
        /* Make sure that the masq does not free our PVT for the old call */
        if (! earlyreplace && ! oneleggedreplace )
                ast_set_flag(&p->refer->refer_call->flags[0], SIP_DEFER_BYE_ON_TRANSFER);       /* Delay hangup */
-               
+
        /* Prepare the masquerade - if this does not happen, we will be gone */
        if(ast_channel_masquerade(replacecall, c))
                ast_log(LOG_ERROR, "Failed to masquerade C into Replacecall\n");
        else
                ast_debug(4, "Invite/Replaces: Going to masquerade %s into %s\n", c->name, replacecall->name);
 
-       /* The masquerade will happen as soon as someone reads a frame from the channel */
-
        /* C should now be in place of replacecall */
-       /* ast_read needs to lock channel */
-       ast_channel_unlock(c);
-       
+       if (ast_do_masquerade(replacecall)) {
+               ast_log(LOG_WARNING, "Failed to perform masquerade with INVITE replaces\n");
+       }
+
        if (earlyreplace || oneleggedreplace ) {
-               /* Force the masq to happen */
-               if ((f = ast_read(replacecall))) {      /* Force the masq to happen */
-                       ast_frfree(f);
-                       f = NULL;
-                       ast_debug(4, "Invite/Replace:  Could successfully read frame from RING channel!\n");
-               } else {
-                       ast_log(LOG_WARNING, "Invite/Replace:  Could not read frame from RING channel \n");
-               }
                c->hangupcause = AST_CAUSE_SWITCH_CONGESTION;
-               if (!oneleggedreplace)
-                       ast_channel_unlock(replacecall);
-       } else {        /* Bridged call, UP channel */
-               if ((f = ast_read(replacecall))) {      /* Force the masq to happen */
-                       /* Masq ok */
-                       ast_frfree(f);
-                       f = NULL;
-                       ast_debug(3, "Invite/Replace:  Could successfully read frame from channel! Masq done.\n");
-               } else {
-                       ast_log(LOG_WARNING, "Invite/Replace:  Could not read frame from channel. Transfer failed\n");
-               }
-               ast_channel_unlock(replacecall);
        }
-       sip_pvt_unlock(p->refer->refer_call);
 
        ast_setstate(c, AST_STATE_DOWN);
        ast_debug(4, "After transfer:----------------------------\n");
@@ -19775,13 +19752,18 @@ static int handle_invite_replaces(struct sip_pvt *p, struct sip_request *req, in
                ast_debug(4, " -- No channel yet \n");
        ast_debug(4, "End After transfer:----------------------------\n");
 
-       ast_channel_unlock(p->owner);   /* Unlock new owner */
-       if (!oneleggedreplace)
-               sip_pvt_unlock(p);      /* Unlock SIP structure */
+       /* unlock sip pvt and owner so hangup can do its thing */
+       ast_channel_unlock(replacecall);
+       ast_channel_unlock(c);
+       sip_pvt_unlock(p->refer->refer_call);
+       sip_pvt_unlock(p);
+       *nounlock = 1;
 
        /* The call should be down with no ast_channel, so hang it up */
        c->tech_pvt = dialog_unref(c->tech_pvt, "unref dialog c->tech_pvt");
        ast_hangup(c);
+       sip_pvt_lock(p); /* lock PVT structure again after hangup */
+
        return 0;
 }
 
@@ -20747,7 +20729,7 @@ static int handle_request_invite(struct sip_pvt *p, struct sip_request *req, int
                        /* Go and take over the target call */
                        if (sipdebug)
                                ast_debug(4, "Sending this call to the invite/replcaes handler %s\n", p->callid);
-                       return handle_invite_replaces(p, req, debug, seqno, sin);
+                       return handle_invite_replaces(p, req, debug, seqno, sin, nounlock);
                }
        }