Index: pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 4561)
+++ pjproject/trunk/pjmedia/include/pjmedia/sdp_neg.h	(revision 4562)
@@ -53,13 +53,13 @@
  *     ----------------------->| LOCAL_OFFER |<-----------------------
  *    |                        +-------------+______                  |
- *    |                               |             \______ cancel()  |
- *    |           set_remote_answer() |                    \______    |
- *    |                               V                            \  |
+ *    |                               |             \_____________    |
+ *    |           set_remote_answer() |           cancel_offer()  \   |
+ *    |                               V                            v  |
  * +--+---+                     +-----------+     negotiate()     +-~----+
  * | NULL |                     | WAIT_NEGO |-------------------->| DONE |
  * +------+                     +-----------+                     +------+
- *    |                               A                               |
- *    |            set_local_answer() |                               |
- *    |                               |                               |
+ *    |                               A      ______________________^  |
+ *    |            set_local_answer() |     /     cancel_offer()      |
+ *    |                               |    /                          |
  *    |                        +--------------+   set_remote_offer()  |
  *     ----------------------->| REMOTE_OFFER |<----------------------
Index: pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 4561)
+++ pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h	(revision 4562)
@@ -160,4 +160,29 @@
     void (*on_rx_offer)(pjsip_inv_session *inv,
 			const pjmedia_sdp_session *offer);
+
+    /**
+     * This callback is optional, and is called when the invite session has
+     * received a re-INVITE from the peer. It will be called after
+     * on_rx_offer() callback and works only for re-INVITEs. It allows more
+     * fine-grained control over the response to a re-INVITE, e.g. sending
+     * a provisional response first. Application can return PJ_SUCCESS and
+     * send a reply using the function #pjsip_inv_initial_answer() or
+     * #pjsip_inv_answer(), as with the initial INVITE. If application
+     * returns non-PJ_SUCCESS, it needs to set the SDP answer with
+     * #pjsip_inv_set_sdp_answer() and the re-INVITE will be answered
+     * automatically.
+     *
+     * @param inv	The invite session.
+     * @param offer	Remote offer.
+     * @param rdata     The received re-INVITE request.
+     *
+     * @return		- PJ_SUCCESS: application will answer the re-INVITE
+     *                    manually
+     *                  - non-PJ_SUCCESS: answer the re-INVITE automatically
+     *                    using the SDP set via #pjsip_inv_set_sdp_answer()
+     */
+    pj_status_t (*on_rx_reinvite)(pjsip_inv_session *inv,
+    		                  const pjmedia_sdp_session *offer,
+                                  pjsip_rx_data *rdata);
 
     /**
@@ -699,7 +724,5 @@
 
 /**
- * Create a response message to the initial INVITE request. This function
- * can only be called for the initial INVITE request, as subsequent
- * re-INVITE request will be answered automatically.
+ * Create a response message to an INVITE request.
  *
  * @param inv		The UAS invite session.
@@ -798,4 +821,22 @@
 
 
+/**
+ * Create a CANCEL request for an ongoing re-INVITE transaction. If no
+ * provisional response has been received, the function will not create
+ * CANCEL request (the function will return PJ_SUCCESS but the \a p_tdata
+ * will contain NULL) because we cannot send CANCEL before receiving
+ * provisional response. If then a provisional response is received,
+ * the invite session will send CANCEL automatically.
+ *
+ * @param inv		The invite session.
+ * @param p_tdata	Pointer to receive the message to be created. Note
+ *			that it's possible to receive NULL here while the
+ *			function returns PJ_SUCCESS, see the description.
+ *
+ * @return		PJ_SUCCESS if termination is initiated.
+ */
+PJ_DECL(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv,
+					        pjsip_tx_data **p_tdata );
+
 
 /**
Index: pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 4561)
+++ pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c	(revision 4562)
@@ -1836,4 +1836,13 @@
 		PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
 	{
+            if (mod_inv.cb.on_rx_reinvite && inv->notify &&
+                msg->type == PJSIP_REQUEST_MSG &&
+                msg->line.req.method.id == PJSIP_INVITE_METHOD)
+            {
+                /* Do not return failure first, allow the application
+                 * to set the answer in the on_rx_reinvite() callback.
+                 */
+                return PJ_SUCCESS;
+            }
 	    return PJ_EINVALIDOP;
 	}
@@ -1973,4 +1982,14 @@
     }
 
+    /* Cancel SDP negotiation if this is a negative reply to a re-INVITE */
+    if (st_code >= 300 && inv->neg != NULL &&
+        inv->state == PJSIP_INV_STATE_CONFIRMED)
+    {
+        pjmedia_sdp_neg_state neg_state;
+        neg_state = pjmedia_sdp_neg_get_state(inv->neg);
+        if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER) {
+            pjmedia_sdp_neg_cancel_offer(inv->neg);
+        }
+    }
 
     return PJ_SUCCESS;
@@ -2053,6 +2072,5 @@
 
 /*
- * Answer initial INVITE
- * Re-INVITE will be answered automatically, and will not use this function.
+ * Answer INVITE request.
  */ 
 PJ_DEF(pj_status_t) pjsip_inv_answer(	pjsip_inv_session *inv,
@@ -2277,4 +2295,60 @@
 
     pj_log_pop_indent();
+    return PJ_SUCCESS;
+}
+
+/*
+ * Cancel re-INVITE transaction.
+ */
+PJ_DEF(pj_status_t) pjsip_inv_cancel_reinvite( pjsip_inv_session *inv,
+                                               pjsip_tx_data **p_tdata )
+{
+    pjsip_tx_data *tdata;
+    pj_status_t status;
+
+    /* Verify arguments. */
+    PJ_ASSERT_RETURN(inv && p_tdata, PJ_EINVAL);
+    
+    pj_log_push_indent();
+
+    /* Create appropriate message. */
+    switch (inv->state) {
+    case PJSIP_INV_STATE_CONFIRMED:
+        /* MUST have the original UAC INVITE transaction  */
+        PJ_ASSERT_RETURN(inv->invite_tsx != NULL, PJ_EBUG);
+
+        /* CANCEL should only be called when we have received a
+         * provisional response.
+         */
+        if (inv->invite_tsx->status_code < 100) {
+            inv->cancelling = PJ_TRUE;
+            inv->pending_cancel = PJ_TRUE;
+            *p_tdata = NULL;
+            PJ_LOG(4, (inv->obj_name, "Delaying CANCEL since no "
+                       "provisional response is received yet"));
+            pj_log_pop_indent();
+            return PJ_SUCCESS;
+        }
+
+        status = pjsip_endpt_create_cancel(inv->dlg->endpt, 
+                                           inv->invite_tsx->last_tx,
+                                           &tdata);
+        if (status != PJ_SUCCESS) {
+            pj_log_pop_indent();
+            return status;
+        }
+	break;
+
+    default:
+        /* We cannot send CANCEL to a re-INVITE if the INVITE session is
+         * not confirmed.
+         */
+        pj_log_pop_indent();
+        return PJ_EINVALIDOP;
+    }
+
+    pj_log_pop_indent();
+
+    *p_tdata = tdata;
     return PJ_SUCCESS;
 }
@@ -4083,15 +4157,7 @@
 
 	/*
-	 * Handle strandled incoming CANCEL.
-	 */
-	pjsip_rx_data *rdata = e->body.tsx_state.src.rdata;
-	pjsip_tx_data *tdata;
-	pj_status_t status;
-
-	status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata);
-	if (status != PJ_SUCCESS) return;
-
-	status = pjsip_dlg_send_response(dlg, tsx, tdata);
-	if (status != PJ_SUCCESS) return;
+	 * Handle strandled incoming CANCEL or CANCEL for re-INVITE
+	 */
+        inv_respond_incoming_cancel(inv, tsx, e);
 
     } else if (tsx->role == PJSIP_ROLE_UAS &&
@@ -4229,5 +4295,5 @@
 	    pjsip_tx_data *tdata;
 	    pj_status_t status;
-	    pjsip_rdata_sdp_info *sdp_info;
+	    pjsip_rdata_sdp_info *sdp_info = NULL;
 	    pjsip_status_code st_code;
 
@@ -4302,4 +4368,27 @@
 	    status = inv_check_sdp_in_incoming_msg(inv, tsx, rdata);
 
+            if (status == PJ_SUCCESS && mod_inv.cb.on_rx_reinvite &&
+                inv->notify)
+            {
+	        sdp_info = pjsip_rdata_get_sdp_info(rdata);
+                if ((*mod_inv.cb.on_rx_reinvite)
+                        (inv, sdp_info->sdp, rdata) == PJ_SUCCESS)
+                {
+                    /* Application will send its own response.
+                     * Our job is done. */
+                    return;
+                }
+
+                /* If application lets us answer the re-INVITE,
+                 * application must set the SDP answer with
+                 * #pjsip_inv_set_sdp_answer().
+                 */
+                if (pjmedia_sdp_neg_get_state(inv->neg) !=
+		    PJMEDIA_SDP_NEG_STATE_WAIT_NEGO)
+                {
+                    status = PJ_EINVALIDOP;
+                }
+            }
+
 	    if (status != PJ_SUCCESS) {
 
@@ -4343,5 +4432,6 @@
 	     * Otherwise generate offer from local active SDP.
 	     */
-	    sdp_info = pjsip_rdata_get_sdp_info(rdata);
+            if (!sdp_info)
+                sdp_info = pjsip_rdata_get_sdp_info(rdata);
 	    if (sdp_info->sdp != NULL) {
 		status = process_answer(inv, 200, tdata, NULL);
@@ -4460,4 +4550,18 @@
 	    inv->invite_tsx = tsx;
 
+        } else if (tsx->state == PJSIP_TSX_STATE_PROCEEDING) {
+            
+            /* CANCEL the re-INVITE if necessary */
+            if (inv->pending_cancel) {
+	        pj_status_t status;
+		pjsip_tx_data *cancel;
+
+		inv->pending_cancel = PJ_FALSE;
+
+		status = pjsip_inv_cancel_reinvite(inv, &cancel);
+		if (status == PJ_SUCCESS && cancel)
+		    status = pjsip_inv_send_msg(inv, cancel);
+            }
+
 	} else if (tsx->state == PJSIP_TSX_STATE_TERMINATED &&
 		   tsx->status_code/100 == 2) 
