- Added option to send empty Authorization header in outgoing requests
- When UAS has sent answer in reliable 1xx, do not put SDP in 2xx
- Handle the case when UPDATE is challenged with 401/407
- Obsolete --service-route option in pjsua



git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1561 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c
index 5ce657d..8250b04 100644
--- a/pjsip/src/pjsip-ua/sip_inv.c
+++ b/pjsip/src/pjsip-ua/sip_inv.c
@@ -1563,9 +1563,19 @@
 
     /* Include SDP when it's available for 2xx and 18x (but not 180) response.
      * Subsequent response will include this SDP.
+     *
+     * Note note:
+     *	- When offer/answer has been completed in reliable 183, we MUST NOT
+     *	  send SDP in 2xx response. So if we don't have SDP to send, clear
+     *	  the SDP in the message body ONLY if 100rel is active in this 
+     *    session.
      */
     if (sdp) {
 	tdata->msg->body = create_sdp_body(tdata->pool, sdp);
+    } else {
+	if (inv->options & PJSIP_INV_REQUIRE_100REL) {
+	    tdata->msg->body = NULL;
+	}
     }
 
 
@@ -2226,8 +2236,32 @@
     struct tsx_inv_data *tsx_inv_data = NULL;
     pj_status_t status = -1;
 
-    /* Process 2xx response */
+    /* Handle 401/407 challenge. */
     if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
+	(tsx->status_code == 401 || tsx->status_code == 407)) {
+
+	pjsip_tx_data *tdata;
+	
+	status = pjsip_auth_clt_reinit_req( &inv->dlg->auth_sess, 
+					    e->body.tsx_state.src.rdata,
+					    tsx->last_tx,
+					    &tdata);
+	
+	if (status != PJ_SUCCESS) {
+	    
+	    /* Does not have proper credentials. 
+	     * End the session anyway.
+	     */
+	    inv_set_cause(inv, PJSIP_SC_OK, NULL);
+	    inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e);
+	    
+	} else {
+	    /* Re-send BYE. */
+	    status = pjsip_inv_send_msg(inv, tdata);
+	}
+
+    /* Process 2xx response */
+    } else if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
 	tsx->status_code/100 == 2 &&
 	e->body.tsx_state.src.rdata->msg_info.msg->body)
     {
diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c
index 3a25747..0a8e6d0 100644
--- a/pjsip/src/pjsip-ua/sip_reg.c
+++ b/pjsip/src/pjsip-ua/sip_reg.c
@@ -307,6 +307,13 @@
     return pjsip_auth_clt_set_credentials(&regc->auth_sess, count, cred);
 }
 
+PJ_DEF(pj_status_t) pjsip_regc_set_prefs( pjsip_regc *regc,
+					  const pjsip_auth_clt_pref *pref)
+{
+    PJ_ASSERT_RETURN(regc && pref, PJ_EINVAL);
+    return pjsip_auth_clt_set_prefs(&regc->auth_sess, pref);
+}
+
 PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc,
 					      const pjsip_route_hdr *route_set)
 {
diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c
index 1c8457b..7fcc1ec 100644
--- a/pjsip/src/pjsip/sip_auth_client.c
+++ b/pjsip/src/pjsip/sip_auth_client.c
@@ -532,6 +532,36 @@
 }
 
 
+/*
+ * Set the preference for the client authentication session.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_clt_set_prefs(pjsip_auth_clt_sess *sess,
+					     const pjsip_auth_clt_pref *p)
+{
+    PJ_ASSERT_RETURN(sess && p, PJ_EINVAL);
+
+    pj_memcpy(&sess->pref, p, sizeof(*p));
+    pj_strdup(sess->pool, &sess->pref.algorithm, &p->algorithm);
+    //if (sess->pref.algorithm.slen == 0)
+    //	sess->pref.algorithm = pj_str("md5");
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * Get the preference for the client authentication session.
+ */
+PJ_DEF(pj_status_t) pjsip_auth_clt_get_prefs(pjsip_auth_clt_sess *sess,
+					     pjsip_auth_clt_pref *p)
+{
+    PJ_ASSERT_RETURN(sess && p, PJ_EINVAL);
+
+    pj_memcpy(p, &sess->pref, sizeof(pjsip_auth_clt_pref));
+    return PJ_SUCCESS;
+}
+
+
 /* 
  * Create Authorization/Proxy-Authorization response header based on the challege
  * in WWW-Authenticate/Proxy-Authenticate header.
@@ -698,6 +728,22 @@
 #endif
 
 
+/* Find credential in list of (Proxy-)Authorization headers */
+static pjsip_authorization_hdr* get_header_for_realm(const pjsip_hdr *hdr_list,
+						     const pj_str_t *realm)
+{
+    pjsip_authorization_hdr *h;
+
+    h = (pjsip_authorization_hdr*)hdr_list->next;
+    while (h != (pjsip_authorization_hdr*)hdr_list) {
+	if (pj_stricmp(&h->credential.digest.realm, realm)==0)
+	    return h;
+	h = h->next;
+    }
+
+    return NULL;
+}
+
 
 /* Initialize outgoing request. */
 PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
@@ -705,12 +751,16 @@
 {
     const pjsip_method *method;
     pjsip_cached_auth *auth;
+    pjsip_hdr added;
 
     PJ_ASSERT_RETURN(sess && tdata, PJ_EINVAL);
     PJ_ASSERT_RETURN(sess->pool, PJSIP_ENOTINITIALIZED);
     PJ_ASSERT_RETURN(tdata->msg->type==PJSIP_REQUEST_MSG,
 		     PJSIP_ENOTREQUESTMSG);
 
+    /* Init list */
+    pj_list_init(&added);
+
     /* Get the method. */
     method = &tdata->msg->line.req.method;
 
@@ -728,7 +778,8 @@
 		    if (pjsip_method_cmp(&entry->method, method)==0) {
 			pjsip_authorization_hdr *hauth;
 			hauth = pjsip_hdr_shallow_clone(tdata->pool, entry->hdr);
-			pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+			//pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+			pj_list_push_back(&added, hauth);
 			break;
 		    }
 		    entry = entry->next;
@@ -776,13 +827,80 @@
 	    if (status != PJ_SUCCESS)
 		return status;
 	    
-	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+	    //pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
+	    pj_list_push_back(&added, hauth);
 	}
 #	endif	/* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */
 
 	auth = auth->next;
     }
 
+    if (sess->pref.initial_auth == PJ_FALSE) {
+	pjsip_hdr *h;
+
+	/* Don't want to send initial empty Authorization header, so
+	 * just send whatever available in the list (maybe empty).
+	 */
+
+	h = added.next;
+	while (h != &added) {
+	    pjsip_hdr *next = h->next;
+	    pjsip_msg_add_hdr(tdata->msg, h);
+	    h = next;
+	}
+    } else {
+	/* For each realm, add either the cached authorization header
+	 * or add an empty authorization header.
+	 */
+	unsigned i;
+	char *uri_str;
+	int len;
+
+	uri_str = pj_pool_alloc(tdata->pool, PJSIP_MAX_URL_SIZE);
+	len = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, tdata->msg->line.req.uri,
+			      uri_str, PJSIP_MAX_URL_SIZE);
+	if (len < 1 || len >= PJSIP_MAX_URL_SIZE)
+	    return PJSIP_EURITOOLONG;
+
+	for (i=0; i<sess->cred_cnt; ++i) {
+	    pjsip_cred_info *c = &sess->cred_info[i];
+	    pjsip_authorization_hdr *h;
+
+	    h = get_header_for_realm(&added, &c->realm);
+	    if (h) {
+		pj_list_erase(h);
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)h);
+	    } else {
+		enum { HDRLEN = 256 };
+		const pj_str_t hname = pj_str("Authorization");
+		pj_str_t hval;
+		pjsip_generic_string_hdr *hs;
+		char *hdr;
+
+		hdr = pj_pool_alloc(tdata->pool, HDRLEN);
+		len = pj_ansi_snprintf(
+		    hdr, HDRLEN,
+		    "%.*s username=\"%.*s\", realm=\"%.*s\","
+		    " nonce=\"\", uri=\"%s\",%s%.*s%s response=\"\"",
+		    (int)c->scheme.slen, c->scheme.ptr,
+		    (int)c->username.slen, c->username.ptr,
+		    (int)c->realm.slen, c->realm.ptr,
+		    uri_str,
+		    (sess->pref.algorithm.slen ? " algorithm=" : ""),
+		    (int)sess->pref.algorithm.slen, sess->pref.algorithm.ptr,
+		    (sess->pref.algorithm.slen ? "," : ""));
+
+		PJ_ASSERT_RETURN(len>0 && len<HDRLEN, PJ_ETOOBIG);
+
+		hval.ptr = hdr;
+		hval.slen = len;
+		hs = pjsip_generic_string_hdr_create(tdata->pool, &hname, 
+						     &hval);
+		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);
+	    }
+	}
+    }
+
     return PJ_SUCCESS;
 }
 
diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c
index 4262d41..6e988f0 100644
--- a/pjsip/src/pjsua-lib/pjsua_acc.c
+++ b/pjsip/src/pjsua-lib/pjsua_acc.c
@@ -544,10 +544,6 @@
     pjsip_uri *uri[PJSUA_ACC_MAX_PROXIES];
     unsigned i, uri_cnt = 0, rcnt;
 
-    /* Skip processing is enable_service_route is not set */
-    if (!acc->cfg.enable_service_route)
-	return;
-
     /* Find and parse Service-Route headers */
     for (;;) {
 	char saved;
@@ -916,6 +912,9 @@
 	pjsip_regc_set_credentials( acc->regc, acc->cred_cnt, acc->cred);
     }
 
+    /* Set authentication preference */
+    pjsip_regc_set_prefs(acc->regc, &acc->cfg.auth_pref);
+
     /* Set route-set
      */
     if (!pj_list_empty(&acc->route_set)) {
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index f0f5a61..a7882b0 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -383,6 +383,8 @@
 					acc->cred_cnt, acc->cred);
     }
 
+    /* Set authentication preference */
+    pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
 
     /* Create initial INVITE: */
 
@@ -666,6 +668,10 @@
 				       pjsua_var.acc[acc_id].cred);
     }
 
+    /* Set preference */
+    pjsip_auth_clt_set_prefs(&dlg->auth_sess, 
+			     &pjsua_var.acc[acc_id].cfg.auth_pref);
+
     /* Create invite session: */
     status = pjsip_inv_create_uas( dlg, rdata, answer, options, &inv);
     if (status != PJ_SUCCESS) {
diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c
index 48cb802..6eaf2b4 100644
--- a/pjsip/src/pjsua-lib/pjsua_im.c
+++ b/pjsip/src/pjsua-lib/pjsua_im.c
@@ -321,6 +321,9 @@
 		pjsua_var.acc[im_data->acc_id].cred_cnt,
 		pjsua_var.acc[im_data->acc_id].cred);
 
+	    pjsip_auth_clt_set_prefs(&auth, 
+				     &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
+
 	    status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
 					       &tdata);
 	    if (status == PJ_SUCCESS) {
@@ -417,6 +420,9 @@
 		pjsua_var.acc[im_data->acc_id].cred_cnt,
 		pjsua_var.acc[im_data->acc_id].cred);
 
+	    pjsip_auth_clt_set_prefs(&auth, 
+				     &pjsua_var.acc[im_data->acc_id].cfg.auth_pref);
+
 	    status = pjsip_auth_clt_reinit_req(&auth, rdata, tsx->last_tx,
 					       &tdata);
 	    if (status == PJ_SUCCESS) {
diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c
index 1df81ea..d73a00d 100644
--- a/pjsip/src/pjsua-lib/pjsua_pres.c
+++ b/pjsip/src/pjsua-lib/pjsua_pres.c
@@ -571,8 +571,9 @@
 	return PJ_TRUE;
     }
 
-    /* Set credentials. */
+    /* Set credentials and preference. */
     pjsip_auth_clt_set_credentials(&dlg->auth_sess, acc->cred_cnt, acc->cred);
+    pjsip_auth_clt_set_prefs(&dlg->auth_sess, &acc->cfg.auth_pref);
 
     /* Init callback: */
     pj_bzero(&pres_cb, sizeof(pres_cb));
@@ -1173,6 +1174,9 @@
 					acc->cred_cnt, acc->cred);
     }
 
+    /* Set authentication preference */
+    pjsip_auth_clt_set_prefs(&buddy->dlg->auth_sess, &acc->cfg.auth_pref);
+
     pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy);
 
     status = pjsip_pres_initiate(buddy->sub, -1, &tdata);