Index: /pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h	(revision 4585)
+++ /pjproject/trunk/pjsip/include/pjsip-ua/sip_regc.h	(revision 4586)
@@ -83,4 +83,20 @@
 /** Type declaration for callback to receive registration result. */
 typedef void pjsip_regc_cb(struct pjsip_regc_cbparam *param);
+
+/**
+ * Structure to hold parameters when calling application's callback
+ * specified in #pjsip_regc_set_reg_tsx_cb().
+ * To update contact address, application can set the field contact_cnt
+ * and contact inside the callback.
+ */
+struct pjsip_regc_tsx_cb_param
+{
+    struct pjsip_regc_cbparam   cbparam;
+    int                         contact_cnt;
+    pj_str_t                    contact[PJSIP_REGC_MAX_CONTACT];
+};
+
+/** Type declaration for callback set in #pjsip_regc_set_reg_tsx_cb(). */
+typedef void pjsip_regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param);
 
 /**
@@ -192,4 +208,19 @@
 
 /**
+ * Set callback to be called when the registration received a final response.
+ * This callback is different with the one specified during creation via
+ * #pjsip_regc_create(). This callback will be called for any final response
+ * (including 401/407/423) and before any subsequent requests are sent.
+ * In case of unregistration, this callback will not be called.
+ *
+ * @param regc	    The client registration structure.
+ * @param tsx_cb    Pointer to callback function to receive registration status.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_regc_set_reg_tsx_cb(pjsip_regc *regc,
+				               pjsip_regc_tsx_cb *tsx_cb);
+
+/**
  * Set the "sent-by" field of the Via header for outgoing requests.
  *
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4585)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4586)
@@ -567,4 +567,37 @@
 
 } pjsua_create_media_transport_flag;
+
+
+/**
+ * This enumeration specifies the contact rewrite method.
+ */
+typedef enum pjsua_contact_rewrite_method
+{
+    /**
+      * The Contact update will be done by sending unregistration
+      * to the currently registered Contact, while simultaneously sending new
+      * registration (with different Call-ID) for the updated Contact.
+      */
+    PJSUA_CONTACT_REWRITE_UNREGISTER = 1,
+
+    /**
+      * The Contact update will be done in a single, current
+      * registration session, by removing the current binding (by setting its
+      * Contact's expires parameter to zero) and adding a new Contact binding,
+      * all done in a single request.
+      */
+    PJSUA_CONTACT_REWRITE_NO_UNREG = 2,
+
+    /**
+      * The Contact update will be done when receiving any registration final
+      * response. If this flag is not specified, contact update will only be
+      * done upon receiving 2xx response. This flag MUST be used with
+      * PJSUA_CONTACT_REWRITE_UNREGISTER or PJSUA_CONTACT_REWRITE_NO_UNREG
+      * above to specify how the Contact update should be performed when
+      * receiving 2xx response.
+      */
+    PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE = 4
+
+} pjsua_contact_rewrite_method;
 
 
@@ -2541,23 +2574,16 @@
 /**
  * This macro specifies the default value for \a contact_rewrite_method
- * field in pjsua_acc_config. I specifies  how Contact update will be
+ * field in pjsua_acc_config. It specifies how Contact update will be
  * done with the registration, if \a allow_contact_rewrite is enabled in
- *  the account config.
- *
- * If set to 1, the Contact update will be done by sending unregistration
- * to the currently registered Contact, while simultaneously sending new
- * registration (with different Call-ID) for the updated Contact.
- *
- * If set to 2, the Contact update will be done in a single, current
- * registration session, by removing the current binding (by setting its
- * Contact's expires parameter to zero) and adding a new Contact binding,
- * all done in a single request.
- *
- * Value 1 is the legacy behavior.
- *
- * Default value: 2
+ * the account config. See \a pjsua_contact_rewrite_method for the options.
+ *
+ * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior.
+ *
+ * Default value: PJSUA_CONTACT_REWRITE_NO_UNREG(2) |
+ *                PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE(4)
  */
 #ifndef PJSUA_CONTACT_REWRITE_METHOD
-#   define PJSUA_CONTACT_REWRITE_METHOD		2
+#   define PJSUA_CONTACT_REWRITE_METHOD	   (PJSUA_CONTACT_REWRITE_NO_UNREG | \
+                                           PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE)
 #endif
 
@@ -3011,18 +3037,11 @@
     /**
      * Specify how Contact update will be done with the registration, if
-     * \a allow_contact_rewrite is enabled.
-     *
-     * If set to 1, the Contact update will be done by sending unregistration
-     * to the currently registered Contact, while simultaneously sending new
-     * registration (with different Call-ID) for the updated Contact.
-     *
-     * If set to 2, the Contact update will be done in a single, current
-     * registration session, by removing the current binding (by setting its
-     * Contact's expires parameter to zero) and adding a new Contact binding,
-     * all done in a single request.
-     *
-     * Value 1 is the legacy behavior.
-     *
-     * Default value: PJSUA_CONTACT_REWRITE_METHOD (2)
+     * \a allow_contact_rewrite is enabled. The value is bitmask combination of
+     * \a pjsua_contact_rewrite_method. See also pjsua_contact_rewrite_method.
+     *
+     * Value PJSUA_CONTACT_REWRITE_UNREGISTER(1) is the legacy behavior.
+     *
+     * Default value: PJSUA_CONTACT_REWRITE_METHOD
+     * (PJSUA_CONTACT_REWRITE_NO_UNREG | PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE)
      */
     int		     contact_rewrite_method;
Index: /pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c	(revision 4585)
+++ /pjproject/trunk/pjsip/src/pjsip-ua/sip_reg.c	(revision 4586)
@@ -76,4 +76,5 @@
     void			*token;
     pjsip_regc_cb		*cb;
+    pjsip_regc_tsx_cb           *tsx_cb;
 
     pj_str_t			 str_srv_url;
@@ -728,4 +729,24 @@
 }
 
+static void cbparam_init( struct pjsip_regc_cbparam *cbparam,
+                          pjsip_regc *regc, 
+                          pj_status_t status, int st_code, 
+			  const pj_str_t *reason,
+			  pjsip_rx_data *rdata, pj_int32_t expiration,
+			  int contact_cnt, pjsip_contact_hdr *contact[])
+{
+    cbparam->regc = regc;
+    cbparam->token = regc->token;
+    cbparam->status = status;
+    cbparam->code = st_code;
+    cbparam->reason = *reason;
+    cbparam->rdata = rdata;
+    cbparam->contact_cnt = contact_cnt;
+    cbparam->expiration = expiration;
+    if (contact_cnt) {
+	pj_memcpy( cbparam->contact, contact, 
+		   contact_cnt*sizeof(pjsip_contact_hdr*));
+    }
+}
 
 static void call_callback(pjsip_regc *regc, pj_status_t status, int st_code, 
@@ -736,21 +757,9 @@
     struct pjsip_regc_cbparam cbparam;
 
-
     if (!regc->cb)
 	return;
 
-    cbparam.regc = regc;
-    cbparam.token = regc->token;
-    cbparam.status = status;
-    cbparam.code = st_code;
-    cbparam.reason = *reason;
-    cbparam.rdata = rdata;
-    cbparam.contact_cnt = contact_cnt;
-    cbparam.expiration = expiration;
-    if (contact_cnt) {
-	pj_memcpy( cbparam.contact, contact, 
-		   contact_cnt*sizeof(pjsip_contact_hdr*));
-    }
-
+    cbparam_init(&cbparam, regc, status, st_code, reason, rdata, expiration,
+                 contact_cnt, contact);
     (*regc->cb)(&cbparam);
 }
@@ -813,4 +822,13 @@
     }
 }
+
+PJ_DEF(pj_status_t) pjsip_regc_set_reg_tsx_cb( pjsip_regc *regc,
+				               pjsip_regc_tsx_cb *tsx_cb)
+{
+    PJ_ASSERT_RETURN(regc, PJ_EINVAL);
+    regc->tsx_cb = tsx_cb;
+    return PJ_SUCCESS;
+}
+
 
 PJ_DEF(pj_status_t) pjsip_regc_set_via_sent_by( pjsip_regc *regc,
@@ -1036,4 +1054,5 @@
     pjsip_transaction *tsx = event->body.tsx_state.tsx;
     pj_bool_t handled = PJ_TRUE;
+    pj_bool_t update_contact = PJ_FALSE;
 
     pj_atomic_inc(regc->busy_ctr);
@@ -1055,4 +1074,47 @@
 	    pjsip_transport_add_ref(regc->last_transport);
 	}
+    }
+
+    if (regc->_delete_flag == 0 && regc->tsx_cb &&
+        regc->current_op == REGC_REGISTERING)
+    {
+        struct pjsip_regc_tsx_cb_param param;
+
+        param.contact_cnt = -1;
+        cbparam_init(&param.cbparam, regc, PJ_SUCCESS, tsx->status_code,
+		     &tsx->status_text,
+                     (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
+	              event->body.tsx_state.src.rdata : NULL,
+                     -1, 0, NULL);
+
+        /* Call regc tsx callback before handling any response */
+        pj_lock_release(regc->lock);
+        (*regc->tsx_cb)(&param);
+        pj_lock_acquire(regc->lock);
+
+        if (param.contact_cnt >= 0) {
+            /* Since we receive non-2xx response, it means that (some) contact
+             * bindings haven't been established so we can safely remove these
+             * contact headers. This is to avoid removing non-existent contact
+             * bindings later.
+             */
+            if (tsx->status_code/100 != 2) {
+                pjsip_contact_hdr *h;
+
+	        h = regc->contact_hdr_list.next;
+	        while (h != &regc->contact_hdr_list) {
+                    pjsip_contact_hdr *next = h->next;
+
+                    if (h->expires == -1) {
+                        pj_list_erase(h);
+                    }
+                    h = next;
+                }
+	    }
+
+            /* Update contact address */
+            pjsip_regc_update_contact(regc, param.contact_cnt, param.contact);
+            update_contact = PJ_TRUE;
+        }
     }
 
@@ -1067,5 +1129,47 @@
 	regc->current_op = REGC_IDLE;
 
-	status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
+        if (update_contact) {
+            pjsip_msg *msg;
+            pjsip_hdr *hdr, *ins_hdr;
+            pjsip_contact_hdr *chdr;
+
+            /* Delete Contact headers, but we shouldn't delete headers
+             * which are supposed to remove contact bindings since
+             * we cannot reconstruct those headers.
+             */
+            msg = tsx->last_tx->msg;
+            hdr = msg->hdr.next;
+            ins_hdr = &msg->hdr;
+            while (hdr != &msg->hdr) {
+                pjsip_hdr *next = hdr->next;
+
+                if (hdr->type == PJSIP_H_CONTACT) {
+                    chdr = (pjsip_contact_hdr *)hdr;
+                    if (chdr->expires != 0) {
+                        pj_list_erase(hdr);
+                        ins_hdr = next;
+                    }
+                }
+                hdr = next;
+            }
+
+            /* Add Contact headers. */
+            chdr = regc->contact_hdr_list.next;
+            while (chdr != &regc->contact_hdr_list) {
+	        pj_list_insert_before(ins_hdr, (pjsip_hdr*)
+                    pjsip_hdr_shallow_clone(tsx->last_tx->pool, chdr));
+	        chdr = chdr->next;
+            }
+
+            /* Also add bindings which are to be removed */
+            while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
+	        chdr = regc->removed_contact_hdr_list.next;
+	        pj_list_insert_before(ins_hdr, (pjsip_hdr*)
+                    pjsip_hdr_clone(tsx->last_tx->pool, chdr));
+	        pj_list_erase(chdr);
+            }
+        }
+
+        status = pjsip_auth_clt_reinit_req( &regc->auth_sess,
 					    rdata, 
 					    tsx->last_tx,  
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 4585)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_acc.c	(revision 4586)
@@ -1495,4 +1495,5 @@
 /* Update NAT address from the REGISTER response */
 static pj_bool_t acc_check_nat_addr(pjsua_acc *acc,
+                                    int contact_rewrite_method,
 				    struct pjsip_regc_cbparam *param)
 {
@@ -1679,10 +1680,11 @@
 			 via_addr->ptr,
 			 rport,
-			 acc->cfg.contact_rewrite_method));
-
-    pj_assert(acc->cfg.contact_rewrite_method == 1 ||
-	      acc->cfg.contact_rewrite_method == 2);
-
-    if (acc->cfg.contact_rewrite_method == 1) {
+			 contact_rewrite_method));
+
+    pj_assert(contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER ||
+	      contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG ||
+              contact_rewrite_method == PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE);
+
+    if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_UNREGISTER) {
 	/* Unregister current contact */
 	pjsua_acc_set_registration(acc->index, PJ_FALSE);
@@ -1762,10 +1764,14 @@
     }
 
-    if (acc->cfg.contact_rewrite_method == 2 && acc->regc != NULL) {
+    if (contact_rewrite_method == PJSUA_CONTACT_REWRITE_NO_UNREG &&
+        acc->regc != NULL)
+    {
 	pjsip_regc_update_contact(acc->regc, 1, &acc->reg_contact);
     }
 
     /* Perform new registration */
-    pjsua_acc_set_registration(acc->index, PJ_TRUE);
+    if (contact_rewrite_method < PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) {
+        pjsua_acc_set_registration(acc->index, PJ_TRUE);
+    }
 
     pj_pool_release(pool);
@@ -2059,4 +2065,35 @@
 }
 
+static void regc_tsx_cb(struct pjsip_regc_tsx_cb_param *param)
+{
+    pjsua_acc *acc = (pjsua_acc*) param->cbparam.token;
+
+    PJSUA_LOCK();
+
+    if (param->cbparam.regc != acc->regc) {
+        PJSUA_UNLOCK();
+	return;
+    }
+
+    pj_log_push_indent();
+
+    if ((acc->cfg.contact_rewrite_method &
+         PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE) ==
+        PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE &&
+        param->cbparam.code >= 400 &&
+        param->cbparam.rdata)
+    {
+        if (acc_check_nat_addr(acc, PJSUA_CONTACT_REWRITE_ALWAYS_UPDATE,
+                               &param->cbparam))
+        {
+            param->contact_cnt = 1;
+            param->contact[0] = acc->reg_contact;
+        }
+    }
+
+    PJSUA_UNLOCK();
+    pj_log_pop_indent();
+}
+
 /*
  * This callback is called by pjsip_regc when outgoing register
@@ -2127,5 +2164,7 @@
 
 	    /* Check NAT bound address */
-	    if (acc_check_nat_addr(acc, param)) {
+            if (acc_check_nat_addr(acc, (acc->cfg.contact_rewrite_method & 3),
+                                   param))
+            {
 		PJSUA_UNLOCK();
 		pj_log_pop_indent();
@@ -2271,4 +2310,6 @@
 	return status;
     }
+
+    pjsip_regc_set_reg_tsx_cb(acc->regc, regc_tsx_cb);
 
     /* If account is locked to specific transport, then set transport to
Index: /pjproject/trunk/tests/pjsua/scripts-sipp/uas-register-ip-change.xml
===================================================================
--- /pjproject/trunk/tests/pjsua/scripts-sipp/uas-register-ip-change.xml	(revision 4585)
+++ /pjproject/trunk/tests/pjsua/scripts-sipp/uas-register-ip-change.xml	(revision 4586)
@@ -8,6 +8,21 @@
   <send>
     <![CDATA[
+      SIP/2.0 100 Trying
+      [last_Via:];received=1.1.1.1;rport=1111
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      WWW-Authenticate: digest realm="test"
+      Content-Length: 0
+    ]]>
+  </send>
+
+  <pause milliseconds="2000"/>
+
+  <send>
+    <![CDATA[
       SIP/2.0 401 Unauthorized
-      [last_Via:];received=1.1.1.1;rport=1111
+      [last_Via:];received=2.2.2.2;rport=2222
       [last_From:]
       [last_To:];tag=[call_number]
@@ -24,6 +39,24 @@
   <send>
     <![CDATA[
+      SIP/2.0 401 Unauthorized again
+      [last_Via:];received=3.3.3.3;rport=3333
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      [last_Contact:]
+      WWW-Authenticate: digest realm="test", stale=true
+      Content-Length: 0
+    ]]>
+  </send>
+
+
+  <recv request="REGISTER" crlf="true">
+  </recv>
+
+  <send>
+    <![CDATA[
       SIP/2.0 200 OK
-      [last_Via:];received=1.1.1.1;rport=1111
+      [last_Via:];received=4.4.4.4;rport=4444
       [last_From:]
       [last_To:];tag=[call_number]
@@ -35,5 +68,40 @@
   </send>
 
-  <!-- REREGISTER -->
+  <!-- IP CHANGE FOR 200/OK -->
+
+  <recv request="REGISTER" crlf="true">
+  </recv>
+
+  <send>
+    <![CDATA[
+      SIP/2.0 401 Unauthorized
+      [last_Via:];received=5.5.5.5;rport=5555
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      [last_Contact:]
+      WWW-Authenticate: digest realm="test"
+      Content-Length: 0
+    ]]>
+  </send>
+
+  <recv request="REGISTER" crlf="true">
+  </recv>
+
+  <send>
+    <![CDATA[
+      SIP/2.0 401 Nasty Unauthorized
+      [last_Via:];received=6.6.6.6;rport=6666
+      [last_From:]
+      [last_To:];tag=[call_number]
+      [last_Call-ID:]
+      [last_CSeq:]
+      [last_Contact:]
+      WWW-Authenticate: digest realm="test", stale=true
+      Content-Length: 0
+    ]]>
+  </send>
+
   <recv request="REGISTER" crlf="true">
   </recv>
@@ -42,5 +110,5 @@
     <![CDATA[
       SIP/2.0 200 OK
-      [last_Via:];received=1.1.1.1;rport=1111
+      [last_Via:];received=8.8.8.8;rport=8888
       [last_From:]
       [last_To:];tag=[call_number]
@@ -48,11 +116,8 @@
       [last_CSeq:]
       [last_Contact:]
-      Subject: Reregister due to IP change, this has two Contacts
       Content-Length: 0
     ]]>
   </send>
 
-
-  <!-- REFRESH, GOT IP CHANGE AGAIN -->
   <recv request="REGISTER" crlf="true">
   </recv>
@@ -61,5 +126,5 @@
     <![CDATA[
       SIP/2.0 200 OK
-      [last_Via:];received=2.2.2.2;rport=2222
+      [last_Via:];received=8.8.8.8;rport=8888
       [last_From:]
       [last_To:];tag=[call_number]
@@ -67,61 +132,4 @@
       [last_CSeq:]
       [last_Contact:]
-      Subject: Refresh, got IP change again
-      Content-Length: 0
-    ]]>
-  </send>
-
-
-  <!-- REREGISTER -->
-  <recv request="REGISTER" crlf="true">
-  </recv>
-
-  <send>
-    <![CDATA[
-      SIP/2.0 200 OK
-      [last_Via:];received=2.2.2.2;rport=2222
-      [last_From:]
-      [last_To:];tag=[call_number]
-      [last_Call-ID:]
-      [last_CSeq:]
-      [last_Contact:]
-      Subject: Reregister due to IP change to 2.2.2.2, this has two Contacts
-      Content-Length: 0
-    ]]>
-  </send>
-
-
-  <!-- WHATEVER -->
-  <recv request="REGISTER" crlf="true">
-  </recv>
-
-  <send>
-    <![CDATA[
-      SIP/2.0 200 OK
-      [last_Via:];received=2.2.2.2;rport=2222
-      [last_From:]
-      [last_To:];tag=[call_number]
-      [last_Call-ID:]
-      [last_CSeq:]
-      [last_Contact:]
-      Subject: Whatever 1
-      Content-Length: 0
-    ]]>
-  </send>
-
-  <!-- WHATEVER -->
-  <recv request="REGISTER" crlf="true">
-  </recv>
-
-  <send>
-    <![CDATA[
-      SIP/2.0 200 OK
-      [last_Via:];received=2.2.2.2;rport=2222
-      [last_From:]
-      [last_To:];tag=[call_number]
-      [last_Call-ID:]
-      [last_CSeq:]
-      [last_Contact:]
-      Subject: Whatever 2
       Content-Length: 0
     ]]>
