Пример #1
0
PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg,
					pjsip_tx_data **p_tdata)
{
    pjsip_msg *msg;
    pjsip_contact_hdr *hdr;
    pj_status_t status;
    pjsip_tx_data *tdata;

    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);

    pj_lock_acquire(regc->lock);

    status = create_request(regc, &tdata);
    if (status != PJ_SUCCESS) {
	pj_lock_release(regc->lock);
	return status;
    }

    msg = tdata->msg;

    /* Add Contact headers. */
    hdr = regc->contact_hdr_list.next;
    while (hdr != &regc->contact_hdr_list) {
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_shallow_clone(tdata->pool, hdr));
	hdr = hdr->next;
    }

    /* Also add bindings which are to be removed */
    while (!pj_list_empty(&regc->removed_contact_hdr_list)) {
	hdr = regc->removed_contact_hdr_list.next;
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_clone(tdata->pool, hdr));
	pj_list_erase(hdr);
    }
    

    if (regc->expires_hdr)
	pjsip_msg_add_hdr(msg, (pjsip_hdr*)
			       pjsip_hdr_shallow_clone(tdata->pool,
						       regc->expires_hdr));

    if (regc->timer.id != 0) {
	pjsip_endpt_cancel_timer(regc->endpt, &regc->timer);
	regc->timer.id = 0;
    }

    regc->auto_reg = autoreg;

    pj_lock_release(regc->lock);

    /* Done */
    *p_tdata = tdata;
    return PJ_SUCCESS;
}
Пример #2
0
PJ_DEF(pj_status_t) pjsip_publishc_publish(pjsip_publishc *pubc, 
					   pj_bool_t auto_refresh,
					   pjsip_tx_data **p_tdata)
{
    pj_status_t status;
    pjsip_tx_data *tdata;

    PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);

    status = create_request(pubc, &tdata);
    if (status != PJ_SUCCESS)
	return status;

    /* Add Expires header */
    if (pubc->expires_hdr) {
	pjsip_hdr *dup;

	dup = (pjsip_hdr*)
	      pjsip_hdr_shallow_clone(tdata->pool, pubc->expires_hdr);
	if (dup)
	    pjsip_msg_add_hdr(tdata->msg, dup);
    }

    /* Cancel existing timer */
    if (pubc->timer.id != 0) {
	pjsip_endpt_cancel_timer(pubc->endpt, &pubc->timer);
	pubc->timer.id = 0;
    }

    pubc->auto_refresh = auto_refresh;

    /* Done */
    *p_tdata = tdata;
    return PJ_SUCCESS;
}
Пример #3
0
static int send_unsolicited_mwi_notify_to_contact(void *obj, void *arg, int flags)
{
	struct unsolicited_mwi_data *mwi_data = arg;
	struct mwi_subscription *sub = mwi_data->sub;
	struct ast_sip_endpoint *endpoint = mwi_data->endpoint;
	pjsip_evsub_state state = mwi_data->state;
	const struct ast_sip_body *body = mwi_data->body;
	struct ast_sip_contact *contact = obj;
	const char *state_name;
	pjsip_tx_data *tdata;
	pjsip_sub_state_hdr *sub_state;
	pjsip_event_hdr *event;
	const pjsip_hdr *allow_events = pjsip_evsub_get_allow_events_hdr(NULL);

	if (ast_sip_create_request("NOTIFY", NULL, endpoint, NULL, contact, &tdata)) {
		ast_log(LOG_WARNING, "Unable to create unsolicited NOTIFY request to endpoint %s URI %s\n", sub->id, contact->uri);
		return 0;
	}

	if (!ast_strlen_zero(endpoint->subscription.mwi.fromuser)) {
		pjsip_fromto_hdr *from = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
		pjsip_name_addr *from_name_addr = (pjsip_name_addr *) from->uri;
		pjsip_sip_uri *from_uri = pjsip_uri_get_uri(from_name_addr->uri);

		pj_strdup2(tdata->pool, &from_uri->user, endpoint->subscription.mwi.fromuser);
	}

	switch (state) {
	case PJSIP_EVSUB_STATE_ACTIVE:
		state_name = "active";
		break;
	case PJSIP_EVSUB_STATE_TERMINATED:
	default:
		state_name = "terminated";
		break;
	}

	sub_state = pjsip_sub_state_hdr_create(tdata->pool);
	pj_cstr(&sub_state->sub_state, state_name);
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) sub_state);

	event = pjsip_event_hdr_create(tdata->pool);
	pj_cstr(&event->event_type, "message-summary");
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr *) event);

	pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, allow_events));
	ast_sip_add_body(tdata, body);
	ast_sip_send_request(tdata, NULL, endpoint, NULL, NULL);

	return 0;
}
Пример #4
0
static pj_status_t create_request(pjsip_regc *regc, 
				  pjsip_tx_data **p_tdata)
{
    pj_status_t status;
    pjsip_tx_data *tdata;

    PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL);

    /* Create the request. */
    status = pjsip_endpt_create_request_from_hdr( regc->endpt, 
						  pjsip_get_register_method(),
						  regc->srv_url,
						  regc->from_hdr,
						  regc->to_hdr,
						  NULL,
						  regc->cid_hdr,
						  regc->cseq_hdr->cseq,
						  NULL,
						  &tdata);
    if (status != PJ_SUCCESS)
	return status;

    /* Add cached authorization headers. */
    pjsip_auth_clt_init_req( &regc->auth_sess, tdata );

    /* Add Route headers from route set, ideally after Via header */
    if (!pj_list_empty(&regc->route_set)) {
	pjsip_hdr *route_pos;
	const pjsip_route_hdr *route;

	route_pos = (pjsip_hdr*)
		    pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
	if (!route_pos)
	    route_pos = &tdata->msg->hdr;

	route = regc->route_set.next;
	while (route != &regc->route_set) {
	    pjsip_hdr *new_hdr = (pjsip_hdr*)
				 pjsip_hdr_shallow_clone(tdata->pool, route);
	    pj_list_insert_after(route_pos, new_hdr);
	    route_pos = new_hdr;
	    route = route->next;
	}
    }

    /* Add additional request headers */
    if (!pj_list_empty(&regc->hdr_list)) {
	const pjsip_hdr *hdr;

	hdr = regc->hdr_list.next;
	while (hdr != &regc->hdr_list) {
	    pjsip_hdr *new_hdr = (pjsip_hdr*)
				 pjsip_hdr_shallow_clone(tdata->pool, hdr);
	    pjsip_msg_add_hdr(tdata->msg, new_hdr);
	    hdr = hdr->next;
	}
    }

    /* Done. */
    *p_tdata = tdata;
    return PJ_SUCCESS;
}
Пример #5
0
static void regc_tsx_callback(void *token, pjsip_event *event)
{
    pj_status_t status;
    pjsip_regc *regc = (pjsip_regc*) token;
    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);
    pj_lock_acquire(regc->lock);

    /* Decrement pending transaction counter. */
    pj_assert(regc->has_tsx);
    regc->has_tsx = PJ_FALSE;

    /* Add reference to the transport */
    if (tsx->transport != regc->last_transport) {
	if (regc->last_transport) {
	    pjsip_transport_dec_ref(regc->last_transport);
	    regc->last_transport = NULL;
	}

	if (tsx->transport) {
	    regc->last_transport = tsx->transport;
	    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;
        }
    }

    /* Handle 401/407 challenge (even when _delete_flag is set) */
    if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED ||
	tsx->status_code == PJSIP_SC_UNAUTHORIZED)
    {
	pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
	pjsip_tx_data *tdata;

	/* reset current op */
	regc->current_op = REGC_IDLE;

        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,  
					    &tdata);

	if (status == PJ_SUCCESS) {
	    status = pjsip_regc_send(regc, tdata);
	}
	
	if (status != PJ_SUCCESS) {

	    /* Only call callback if application is still interested
	     * in it.
	     */
	    if (regc->_delete_flag == 0) {
		/* Should be safe to release the lock temporarily.
		 * We do this to avoid deadlock. 
		 */
		pj_lock_release(regc->lock);
		call_callback(regc, status, tsx->status_code, 
			      &rdata->msg_info.msg->line.status.reason,
			      rdata, -1, 0, NULL);
		pj_lock_acquire(regc->lock);
	    }
	}

    } else if (regc->_delete_flag) {

	/* User has called pjsip_regc_destroy(), so don't call callback. 
	 * This regc will be destroyed later in this function.
	 */

	/* Just reset current op */
	regc->current_op = REGC_IDLE;

    } else if (tsx->status_code == PJSIP_SC_INTERVAL_TOO_BRIEF &&
	       regc->current_op == REGC_REGISTERING)
    {
	/* Handle 423 response automatically:
	 *  - set requested expiration to Min-Expires header, ONLY IF
	 *    the original request is a registration (as opposed to
	 *    unregistration) and the requested expiration was indeed
	 *    lower than Min-Expires)
	 *  - resend the request
	 */
	pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
	pjsip_min_expires_hdr *me_hdr;
	pjsip_tx_data *tdata;
	pj_int32_t min_exp;

	/* reset current op */
	regc->current_op = REGC_IDLE;

	/* Update requested expiration */
	me_hdr = (pjsip_min_expires_hdr*)
		 pjsip_msg_find_hdr(rdata->msg_info.msg,
				    PJSIP_H_MIN_EXPIRES, NULL);
	if (me_hdr) {
	    min_exp = me_hdr->ivalue;
	} else {
	    /* Broken server, Min-Expires doesn't exist.
	     * Just guestimate then, BUT ONLY if if this is the
	     * first time we received such response.
	     */
	    enum {
		/* Note: changing this value would require changing couple of
		 *       Python test scripts.
		 */
		UNSPECIFIED_MIN_EXPIRES = 3601
	    };
	    if (!regc->expires_hdr ||
		 regc->expires_hdr->ivalue != UNSPECIFIED_MIN_EXPIRES)
	    {
		min_exp = UNSPECIFIED_MIN_EXPIRES;
	    } else {
		handled = PJ_FALSE;
		PJ_LOG(4,(THIS_FILE, "Registration failed: 423 response "
				     "without Min-Expires header is invalid"));
		goto handle_err;
	    }
	}

	if (regc->expires_hdr && regc->expires_hdr->ivalue >= min_exp) {
	    /* But we already send with greater expiration time, why does
	     * the server send us with 423? Oh well, just fail the request.
	     */
	    handled = PJ_FALSE;
	    PJ_LOG(4,(THIS_FILE, "Registration failed: invalid "
			         "Min-Expires header value in response"));
	    goto handle_err;
	}

	set_expires(regc, min_exp);

	status = pjsip_regc_register(regc, regc->auto_reg, &tdata);
	if (status == PJ_SUCCESS) {
	    status = pjsip_regc_send(regc, tdata);
	}

	if (status != PJ_SUCCESS) {
	    /* Only call callback if application is still interested
	     * in it.
	     */
	    if (!regc->_delete_flag) {
		/* Should be safe to release the lock temporarily.
		 * We do this to avoid deadlock.
		 */
		pj_lock_release(regc->lock);
		call_callback(regc, status, tsx->status_code,
			      &rdata->msg_info.msg->line.status.reason,
			      rdata, -1, 0, NULL);
		pj_lock_acquire(regc->lock);
	    }
	}

    } else {
	handled = PJ_FALSE;
    }

handle_err:
    if (!handled) {
	pjsip_rx_data *rdata;
	pj_int32_t expiration = NOEXP;
	unsigned contact_cnt = 0;
	pjsip_contact_hdr *contact[PJSIP_REGC_MAX_CONTACT];

	if (tsx->status_code/100 == 2) {

	    rdata = event->body.tsx_state.src.rdata;

	    /* Calculate expiration */
	    expiration = calculate_response_expiration(regc, rdata, 
						       &contact_cnt,
						       PJSIP_REGC_MAX_CONTACT,
						       contact);

	    /* Schedule next registration */
            schedule_registration(regc, expiration);

	} else {
	    rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? 
			event->body.tsx_state.src.rdata : NULL;
	}

	/* Update registration */
	if (expiration==NOEXP) expiration=-1;
	regc->expires = expiration;

	/* Mark operation as complete */
	regc->current_op = REGC_IDLE;

	/* Call callback. */
	/* Should be safe to release the lock temporarily.
	 * We do this to avoid deadlock. 
	 */
	pj_lock_release(regc->lock);
	call_callback(regc, PJ_SUCCESS, tsx->status_code, 
		      (rdata ? &rdata->msg_info.msg->line.status.reason 
			: &tsx->status_text),
		      rdata, expiration, 
		      contact_cnt, contact);
	pj_lock_acquire(regc->lock);
    }

    pj_lock_release(regc->lock);

    /* Delete the record if user destroy regc during the callback. */
    if (pj_atomic_dec_and_get(regc->busy_ctr)==0 && regc->_delete_flag) {
	pjsip_regc_destroy(regc);
    }
}
Пример #6
0
/* Initialize outgoing request. */
PJ_DEF(pj_status_t) pjsip_auth_clt_init_req( pjsip_auth_clt_sess *sess,
					     pjsip_tx_data *tdata )
{
    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;

    auth = sess->cached_auth.next;
    while (auth != &sess->cached_auth) {
	/* Reset stale counter */
	auth->stale_cnt = 0;

	if (auth->qop_value == PJSIP_AUTH_QOP_NONE) {
#	    if defined(PJSIP_AUTH_HEADER_CACHING) && \
	       PJSIP_AUTH_HEADER_CACHING!=0
	    {
		pjsip_cached_auth_hdr *entry = auth->cached_hdr.next;
		while (entry != &auth->cached_hdr) {
		    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);
			pj_list_push_back(&added, hauth);
			break;
		    }
		    entry = entry->next;
		}

#		if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
			   PJSIP_AUTH_AUTO_SEND_NEXT!=0
		{
		    if (entry == &auth->cached_hdr)
			new_auth_for_req( tdata, sess, auth, NULL);
		}
#		endif

	    }
#	    elif defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
		 PJSIP_AUTH_AUTO_SEND_NEXT!=0
	    {
		new_auth_for_req( tdata, sess, auth, NULL);
	    }
#	    endif

	} 
#	if defined(PJSIP_AUTH_QOP_SUPPORT) && \
	   defined(PJSIP_AUTH_AUTO_SEND_NEXT) && \
	   (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
	else if (auth->qop_value == PJSIP_AUTH_QOP_AUTH) {
	    /* For qop="auth", we have to re-create the authorization header. 
	     */
	    const pjsip_cred_info *cred;
	    pjsip_authorization_hdr *hauth;
	    pj_status_t status;

	    cred = auth_find_cred(sess, &auth->realm, 
				  &auth->last_chal->scheme);
	    if (!cred) {
		auth = auth->next;
		continue;
	    }

	    status = auth_respond( tdata->pool, auth->last_chal, 
				   tdata->msg->line.req.uri, 
				   cred,
				   &tdata->msg->line.req.method,
				   sess->pool, auth, &hauth);
	    if (status != PJ_SUCCESS)
		return status;
	    
	    //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 = (char*)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 {
		pjsip_authorization_hdr *hs;

		hs = pjsip_authorization_hdr_create(tdata->pool);
		pj_strdup(tdata->pool, &hs->scheme, &c->scheme);
		pj_strdup(tdata->pool, &hs->credential.digest.username,
			  &c->username);
		pj_strdup(tdata->pool, &hs->credential.digest.realm,
			  &c->realm);
		pj_strdup2(tdata->pool, &hs->credential.digest.uri,
			   uri_str);
		pj_strdup(tdata->pool, &hs->credential.digest.algorithm,
			  &sess->pref.algorithm);

		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hs);
	    }
	}
    }

    return PJ_SUCCESS;
}
Пример #7
0
static pj_status_t create_request(pjsip_publishc *pubc, 
				  pjsip_tx_data **p_tdata)
{
    const pj_str_t STR_EVENT = { "Event", 5 };
    pj_status_t status;
    pjsip_generic_string_hdr *hdr;
    pjsip_tx_data *tdata;

    PJ_ASSERT_RETURN(pubc && p_tdata, PJ_EINVAL);

    /* Create the request. */
    status = pjsip_endpt_create_request_from_hdr( pubc->endpt, 
						  &pjsip_publish_method,
						  pubc->target_uri,
						  pubc->from_hdr,
						  pubc->to_hdr,
						  NULL,
						  pubc->cid_hdr,
						  pubc->cseq_hdr->cseq,
						  NULL,
						  &tdata);
    if (status != PJ_SUCCESS)
	return status;

    /* Add cached authorization headers. */
    pjsip_auth_clt_init_req( &pubc->auth_sess, tdata );

    /* Add Route headers from route set, ideally after Via header */
    if (!pj_list_empty(&pubc->route_set)) {
	pjsip_hdr *route_pos;
	const pjsip_route_hdr *route;

	route_pos = (pjsip_hdr*)
		    pjsip_msg_find_hdr(tdata->msg, PJSIP_H_VIA, NULL);
	if (!route_pos)
	    route_pos = &tdata->msg->hdr;

	route = pubc->route_set.next;
	while (route != &pubc->route_set) {
	    pjsip_hdr *new_hdr = (pjsip_hdr*)
	    			 pjsip_hdr_shallow_clone(tdata->pool, route);
	    pj_list_insert_after(route_pos, new_hdr);
	    route_pos = new_hdr;
	    route = route->next;
	}
    }

    /* Add Event header */
    hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_EVENT,
					  &pubc->event);
    if (hdr)
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);


    /* Add SIP-If-Match if we have etag */
    if (pubc->etag.slen) {
	const pj_str_t STR_HNAME = { "SIP-If-Match", 12 };

	hdr = pjsip_generic_string_hdr_create(tdata->pool, &STR_HNAME,
					      &pubc->etag);
	if (hdr)
	    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hdr);
    }


    /* Done. */
    *p_tdata = tdata;
    return PJ_SUCCESS;
}
Пример #8
0
void process_register_request(pjsip_rx_data* rdata)
{
  pj_status_t status;
  int st_code = PJSIP_SC_OK;
  SAS::TrailId trail = get_trail(rdata);

  // Get the URI from the To header and check it is a SIP or SIPS URI.
  pjsip_uri* uri = (pjsip_uri*)pjsip_uri_get_uri(rdata->msg_info.to->uri);

  if ((!PJSIP_URI_SCHEME_IS_SIP(uri)) && (!PJSIP_URI_SCHEME_IS_TEL(uri)))
  {
    // Reject a non-SIP/TEL URI with 404 Not Found (RFC3261 isn't clear
    // whether 404 is the right status code - it says 404 should be used if
    // the AoR isn't valid for the domain in the RequestURI).
    LOG_ERROR("Rejecting register request using invalid URI scheme");

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    // Can't log the public ID as the REGISTER has failed too early
    std::string public_id = "UNKNOWN";
    std::string error_msg = "Rejecting register request using invalid URI scheme";
    event.add_var_param(public_id);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_NOT_FOUND,
                               NULL,
                               NULL,
                               NULL);
    return;
  }

  // Allocate an ACR for this transaction and pass the request to it.
  ACR* acr = acr_factory->get_acr(get_trail(rdata),
                                  CALLING_PARTY,
                                  ACR::requested_node_role(rdata->msg_info.msg));
  acr->rx_request(rdata->msg_info.msg, rdata->pkt_info.timestamp);

  // Canonicalize the public ID from the URI in the To header.
  std::string public_id = PJUtils::public_id_from_uri(uri);

  LOG_DEBUG("Process REGISTER for public ID %s", public_id.c_str());

  // Get the call identifier and the cseq number from the respective headers.
  std::string cid = PJUtils::pj_str_to_string((const pj_str_t*)&rdata->msg_info.cid->id);;
  pjsip_msg *msg = rdata->msg_info.msg;

  // Add SAS markers to the trail attached to the message so the trail
  // becomes searchable.
  LOG_DEBUG("Report SAS start marker - trail (%llx)", trail);
  SAS::Marker start_marker(trail, MARKER_ID_START, 1u);
  SAS::report_marker(start_marker);

  PJUtils::report_sas_to_from_markers(trail, rdata->msg_info.msg);
  PJUtils::mark_sas_call_branch_ids(trail, NULL, rdata->msg_info.msg);

  // Query the HSS for the associated URIs.
  std::vector<std::string> uris;
  std::map<std::string, Ifcs> ifc_map;
  std::string private_id;
  std::string private_id_for_binding;
  bool success = get_private_id(rdata, private_id);
  if (!success)
  {
    // There are legitimate cases where we don't have a private ID
    // here (for example, on a re-registration where Bono has set the
    // Integrity-Protected header), so this is *not* a failure
    // condition.

    // We want the private ID here so that Homestead can use it to
    // subscribe for updates from the HSS - but on a re-registration,
    // Homestead should already have subscribed for updates during the
    // initial registration, so we can just make a request using our
    // public ID.
    private_id = "";

    // IMS compliant clients will always have the Auth header on all REGISTERs,
    // including reREGISTERS. Non-IMS clients won't, but their private ID
    // will always be the public ID with the sip: removed.
    private_id_for_binding = PJUtils::default_private_id_from_uri(uri);
  }
  else
  {
    private_id_for_binding = private_id;
  }

  SAS::Event event(trail, SASEvent::REGISTER_START, 0);
  event.add_var_param(public_id);
  event.add_var_param(private_id);
  SAS::report_event(event);

  std::string regstate;
  std::deque<std::string> ccfs;
  std::deque<std::string> ecfs;
  HTTPCode http_code = hss->update_registration_state(public_id,
                                                      private_id,
                                                      HSSConnection::REG,
                                                      regstate,
                                                      ifc_map,
                                                      uris,
                                                      ccfs,
                                                      ecfs,
                                                      trail);
  if ((http_code != HTTP_OK) || (regstate != HSSConnection::STATE_REGISTERED))
  {
    // We failed to register this subscriber at the HSS.  This indicates that the
    // HSS is unavailable, the public identity doesn't exist or the public
    // identity doesn't belong to the private identity.

    // The client shouldn't retry when the subscriber isn't present in the
    // HSS; reject with a 403 in this case.
    //
    // The client should retry on timeout but no other Clearwater nodes should
    // (as Sprout will already have retried on timeout). Reject with a 504
    // (503 is used for overload).
    st_code = PJSIP_SC_SERVER_TIMEOUT;

    if (http_code == HTTP_NOT_FOUND)
    {
      st_code = PJSIP_SC_FORBIDDEN;
    }

    LOG_ERROR("Rejecting register request with invalid public/private identity");

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Rejecting register request with invalid public/private identity";
    event.add_var_param(error_msg);
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               st_code,
                               NULL,
                               NULL,
                               NULL);
    delete acr;
    return;
  }

  // Determine the AOR from the first entry in the uris array.
  std::string aor = uris.front();
  LOG_DEBUG("REGISTER for public ID %s uses AOR %s", public_id.c_str(), aor.c_str());

  // Get the system time in seconds for calculating absolute expiry times.
  int now = time(NULL);
  int expiry = 0;
  bool is_initial_registration;

  // Loop through each contact header. If every registration is an emergency
  // registration and its expiry is 0 then reject with a 501.
  // If there are valid registration updates to make then attempt to write to
  // store, which also stops emergency registrations from being deregistered.
  bool reject_with_501 = true;
  bool any_emergency_registrations = false;
  bool reject_with_400 = false;
  pjsip_contact_hdr* contact_hdr = (pjsip_contact_hdr*)
                 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, NULL);

  while (contact_hdr != NULL)
  {
    pjsip_expires_hdr* expires = (pjsip_expires_hdr*)pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
    expiry = (contact_hdr->expires != -1) ? contact_hdr->expires :
             (expires != NULL) ? expires->ivalue :
              max_expires;

    if ((contact_hdr->star) && (expiry != 0))
    {
      // Wildcard contact, which can only be used if the expiry is 0
      LOG_ERROR("Attempted to deregister all bindings, but expiry value wasn't 0");
      reject_with_400 = true;
      break;
    }

    reject_with_501 = (reject_with_501 &&
                       PJUtils::is_emergency_registration(contact_hdr) && (expiry == 0));
    any_emergency_registrations = (any_emergency_registrations ||
                                  PJUtils::is_emergency_registration(contact_hdr));

    contact_hdr = (pjsip_contact_hdr*) pjsip_msg_find_hdr(rdata->msg_info.msg,
                                                          PJSIP_H_CONTACT,
                                                          contact_hdr->next);
  }

  if (reject_with_400)
  {
    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Rejecting register request with invalid contact header";
    event.add_var_param(error_msg);
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_BAD_REQUEST,
                               NULL,
                               NULL,
                               NULL);
    delete acr;
    return;
  }

  if (reject_with_501)
  {
    LOG_ERROR("Rejecting register request as attempting to deregister an emergency registration");

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Rejecting deregister request for emergency registrations";
    event.add_var_param(error_msg);
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_NOT_IMPLEMENTED,
                               NULL,
                               NULL,
                               NULL);
    delete acr;
    return;
  }


  // Write to the local store, checking the remote store if there is no entry locally.
  RegStore::AoR* aor_data = write_to_store(store, aor, rdata, now, expiry,
                                           is_initial_registration, NULL, remote_store,
                                           true, private_id_for_binding, trail);
  if (aor_data != NULL)
  {
    // Log the bindings.
    log_bindings(aor, aor_data);

    // If we have a remote store, try to store this there too.  We don't worry
    // about failures in this case.
    if (remote_store != NULL)
    {
      int tmp_expiry = 0;
      bool ignored;
      RegStore::AoR* remote_aor_data = write_to_store(remote_store, aor, rdata, now,
                                                      tmp_expiry, ignored, aor_data,
                                                      NULL, false, private_id_for_binding,
                                                      trail);
      delete remote_aor_data;
    }
  }
  else
  {
    // Failed to connect to the local store.  Reject the register with a 500
    // response.
    // LCOV_EXCL_START - the can't fail to connect to the store we use for UT
    st_code = PJSIP_SC_INTERNAL_SERVER_ERROR;

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Unable to access Registration Store";
    event.add_var_param(error_msg);
    SAS::report_event(event);

    // LCOV_EXCL_STOP
  }

  // Build and send the reply.
  pjsip_tx_data* tdata;
  status = PJUtils::create_response(stack_data.endpt, rdata, st_code, NULL, &tdata);
  if (status != PJ_SUCCESS)
  {
    // LCOV_EXCL_START - don't know how to get PJSIP to fail to create a response
    std::string error_msg = "Error building REGISTER " + std::to_string(status) +
                            " response " + PJUtils::pj_status_to_string(status);

    LOG_ERROR(error_msg.c_str());

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    PJUtils::respond_stateless(stack_data.endpt,
                               rdata,
                               PJSIP_SC_INTERNAL_SERVER_ERROR,
                               NULL,
                               NULL,
                               NULL);
    delete acr;
    delete aor_data;
    return;
    // LCOV_EXCL_STOP
  }

  if (st_code != PJSIP_SC_OK)
  {
    // LCOV_EXCL_START - we only reject REGISTER if something goes wrong, and
    // we aren't covering any of those paths so we can't hit this either
    status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL);

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "REGISTER failed with status code: " + std::to_string(st_code);
    event.add_var_param(error_msg);
    SAS::report_event(event);

    delete acr;
    delete aor_data;
    return;
    // LCOV_EXCL_STOP
  }

  // Add supported and require headers for RFC5626.
  pjsip_generic_string_hdr* gen_hdr;
  gen_hdr = pjsip_generic_string_hdr_create(tdata->pool,
                                            &STR_SUPPORTED,
                                            &STR_OUTBOUND);
  if (gen_hdr == NULL)
  {
    // LCOV_EXCL_START - can't see how this could ever happen
    LOG_ERROR("Failed to add RFC 5626 headers");

    SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
    event.add_var_param(public_id);
    std::string error_msg = "Failed to add RFC 5636 headers";
    event.add_var_param(error_msg);
    SAS::report_event(event);

    tdata->msg->line.status.code = PJSIP_SC_INTERNAL_SERVER_ERROR;
    pjsip_tx_data_invalidate_msg(tdata);
    status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL);
    delete acr;
    delete aor_data;
    return;
    // LCOV_EXCL_STOP
  }
  pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)gen_hdr);

  // Add contact headers for all active bindings.
  for (RegStore::AoR::Bindings::const_iterator i = aor_data->bindings().begin();
       i != aor_data->bindings().end();
       ++i)
  {
    RegStore::AoR::Binding* binding = i->second;
    if (binding->_expires > now)
    {
      // The binding hasn't expired.  Parse the Contact URI from the store,
      // making sure it is formatted as a name-address.
      pjsip_uri* uri = PJUtils::uri_from_string(binding->_uri, tdata->pool, PJ_TRUE);
      if (uri != NULL)
      {
        // Contact URI is well formed, so include this in the response.
        pjsip_contact_hdr* contact = pjsip_contact_hdr_create(tdata->pool);
        contact->star = 0;
        contact->uri = uri;
        contact->q1000 = binding->_priority;
        contact->expires = binding->_expires - now;
        pj_list_init(&contact->other_param);
        for (std::map<std::string, std::string>::iterator j = binding->_params.begin();
             j != binding->_params.end();
             ++j)
        {
          pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
          pj_strdup2(tdata->pool, &new_param->name, j->first.c_str());
          pj_strdup2(tdata->pool, &new_param->value, j->second.c_str());
          pj_list_insert_before(&contact->other_param, new_param);
        }

        // The pub-gruu parameter on the Contact header is calculated
        // from the instance-id, to avoid unnecessary storage in
        // memcached.

        std::string gruu = binding->pub_gruu_quoted_string(tdata->pool);
        if (!gruu.empty())
        {
          pjsip_param *new_param = PJ_POOL_ALLOC_T(tdata->pool, pjsip_param);
          pj_strdup2(tdata->pool, &new_param->name, "pub-gruu");
          pj_strdup2(tdata->pool, &new_param->value, gruu.c_str());
          pj_list_insert_before(&contact->other_param, new_param);
        }

        pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)contact);
      }
      else
      {
        // Contact URI is malformed.  Log an error, but otherwise don't try and
        // fix it.
        // LCOV_EXCL_START hard to hit - needs bad data in the store
        LOG_WARNING("Badly formed contact URI %s for address of record %s",
                    binding->_uri.c_str(), aor.c_str());

        SAS::Event event(trail, SASEvent::REGISTER_FAILED, 0);
        event.add_var_param(public_id);
        std::string error_msg = "Badly formed contact URI - " + binding->_uri;
        event.add_var_param(error_msg);
        SAS::report_event(event);
        // LCOV_EXCL_STOP
      }
    }
  }

  // Deal with path header related fields in the response.
  pjsip_routing_hdr* path_hdr = (pjsip_routing_hdr*)
                              pjsip_msg_find_hdr_by_name(msg, &STR_PATH, NULL);
  if ((path_hdr != NULL) &&
      (!aor_data->bindings().empty()))
  {
    // We have bindings with path headers so we must require outbound.
    pjsip_require_hdr* require_hdr = pjsip_require_hdr_create(tdata->pool);
    require_hdr->count = 1;
    require_hdr->values[0] = STR_OUTBOUND;
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)require_hdr);
  }

  // Echo back any Path headers as per RFC 3327, section 5.3.  We take these
  // from the request as they may not exist in the bindings any more if the
  // bindings have expired.
  while (path_hdr)
  {
    pjsip_msg_add_hdr(tdata->msg,
                      (pjsip_hdr*)pjsip_hdr_clone(tdata->pool, path_hdr));
    path_hdr = (pjsip_routing_hdr*)
                    pjsip_msg_find_hdr_by_name(msg, &STR_PATH, path_hdr->next);
  }

  // Add the Service-Route header.  It isn't safe to do this with the
  // pre-built header from the global pool because the chaining data
  // structures in the header may get overwritten, but it is safe to do a
  // shallow clone.
  pjsip_hdr* clone = (pjsip_hdr*)
                          pjsip_hdr_shallow_clone(tdata->pool, service_route);
  pjsip_msg_insert_first_hdr(tdata->msg, clone);

  // Add P-Associated-URI headers for all of the associated URIs.
  for (std::vector<std::string>::iterator it = uris.begin();
       it != uris.end();
       it++)
  {
    pjsip_routing_hdr* pau =
                        identity_hdr_create(tdata->pool, STR_P_ASSOCIATED_URI);
    pau->name_addr.uri = PJUtils::uri_from_string(*it, tdata->pool);
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)pau);
  }

  // Add a PCFA header.
  PJUtils::add_pcfa_header(tdata->msg, tdata->pool, ccfs, ecfs, true);

  // Pass the response to the ACR.
  acr->tx_response(tdata->msg);

  // Send the response, but prevent the transmitted data from being freed, as we may need to inform the
  // ASes of the 200 OK response we sent.
  pjsip_tx_data_add_ref(tdata);
  status = pjsip_endpt_send_response2(stack_data.endpt, rdata, tdata, NULL, NULL);

  // Send the ACR and delete it.
  acr->send_message();
  delete acr;

  // TODO in sto397: we should do third-party registration once per
  // service profile (i.e. once per iFC, using an arbitrary public
  // ID). hss->get_subscription_data should be enhanced to provide an
  // appropriate data structure (representing the ServiceProfile
  // nodes) and we should loop through that. Don't send any register that
  // contained emergency registrations to the application servers.

  if (!any_emergency_registrations)
  {
    RegistrationUtils::register_with_application_servers(ifc_map[public_id],
                                                         store,
                                                         rdata,
                                                         tdata,
                                                         expiry,
                                                         is_initial_registration,
                                                         public_id,
                                                         trail);
  }

  // Now we can free the tdata.
  pjsip_tx_data_dec_ref(tdata);

  LOG_DEBUG("Report SAS end marker - trail (%llx)", trail);
  SAS::Marker end_marker(trail, MARKER_ID_END, 1u);
  SAS::report_marker(end_marker);
  delete aor_data;
}
Пример #9
0
/*
 * Send notify.
 */
PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
					   pjsip_event_sub_state new_state,
					   const pj_str_t *reason,
					   pjsip_msg_body *body)
{
    pjsip_tx_data *tdata;
    pjsip_sub_state_hdr *ss_hdr;
    const pjsip_route_hdr *route;
    pj_time_val now;
    pj_status_t status;
    pjsip_event_sub_state old_state = sub->state;

    pj_gettimeofday(&now);

    pj_assert(sub->role == PJSIP_ROLE_UAS);
    if (sub->role != PJSIP_ROLE_UAS)
	return -1;

    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", 
			 sub, state[new_state].ptr));

    /* Lock subscription. */
    pj_mutex_lock(sub->mutex);

    /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
    if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
	pj_assert(0);
	pj_mutex_unlock(sub->mutex);
	return -1;
    }

    /* Update state no matter what. */
    sub_set_state(sub, new_state);

    /* Create transmit data. */
    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
						 &NOTIFY,
						 sub->to->uri,
						 sub->from, sub->to,
						 sub->contact, sub->call_id,
						 sub->cseq++,
						 NULL);
    if (!tdata) {
	pj_mutex_unlock(sub->mutex);
	return -1;
    }

    /* Add Event header. */
    pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));

    /* Add Subscription-State header. */
    ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
    ss_hdr->sub_state = state[new_state];
    ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
    if (ss_hdr->expires_param < 0)
	ss_hdr->expires_param = 0;
    if (reason)
	pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
    pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);

    /* Add Allow-Events header. */
    pjsip_msg_add_hdr( tdata->msg, 
		       pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));

    /* Add authentication */
    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
			 sub->cred_cnt, sub->cred_info);

    /* Route set. */
    route = sub->route_set.next;
    while (route != &sub->route_set) {
	pj_list_insert_before( &tdata->msg->hdr,
			       pjsip_hdr_shallow_clone(tdata->pool, route));
	route = route->next;
    }

    /* Attach body. */
    tdata->msg->body = body;

    /* That's it, send! */
    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
    if (status == 0)
	sub->pending_tsx++;

    /* If terminated notify application. */
    if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
	if (sub->cb.on_sub_terminated) {
	    sub->pending_tsx++;
	    (*sub->cb.on_sub_terminated)(sub, reason);
	    sub->pending_tsx--;
	}
    }

    /* Unlock subscription. */
    pj_mutex_unlock(sub->mutex);

    if (status != 0) {
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", 
			     sub, state[sub->state].ptr));
    }

    if (sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    }
    return status;
}
Пример #10
0
/* This function is called when we receive SUBSCRIBE request message 
 * to refresh existing subscription.
 */
static void on_received_sub_refresh( pjsip_event_sub *sub, 
				     pjsip_transaction *tsx, pjsip_rx_data *rdata)
{
    pjsip_event_hdr *e;
    pjsip_expires_hdr *expires;
    pj_str_t hname;
    int status = 200;
    pj_str_t reason_phrase = { NULL, 0 };
    int new_state = sub->state;
    int old_state = sub->state;
    int new_interval = 0;
    pjsip_tx_data *tdata;

    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", 
			 sub, state[sub->state].ptr));

    /* Check that the event matches. */
    hname = pj_str("Event");
    e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
    if (!e) {
	status = 400;
	reason_phrase = pj_str("Missing Event header");
	goto send_response;
    }
    if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
	pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
    {
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");
	goto send_response;
    }

    /* Check server state. */
    if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");
	goto send_response;
    }

    /* Check expires header. */
    expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
    if (!expires) {
	/*
	status = 400;
	reason_phrase = pj_str("Missing Expires header");
	goto send_response;
	*/
	new_interval = sub->default_interval;
    } else {
	/* Check that interval is not too short. 
	 * Note that expires time may be zero (for unsubscription).
	 */
	new_interval = expires->ivalue;
	if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
	    status = PJSIP_SC_INTERVAL_TOO_BRIEF;
	    goto send_response;
	}
    }

    /* Update interval. */
    sub->default_interval = new_interval;
    pj_gettimeofday(&sub->expiry_time);
    sub->expiry_time.sec += new_interval;

    /* Update timer only if this is not unsubscription. */
    if (new_interval > 0) {
	sub->default_interval = new_interval;
	sub_schedule_uas_expire( sub, new_interval );

	/* Call callback. */
	if (sub->cb.on_received_refresh) {
	    sub->pending_tsx++;
	    (*sub->cb.on_received_refresh)(sub, rdata);
	    sub->pending_tsx--;
	}
    }

send_response:
    tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
    if (tdata) {
	if (reason_phrase.slen)
	    tdata->msg->line.status.reason = reason_phrase;

	/* Add Expires header. */
	expires = pjsip_expires_hdr_create(tdata->pool);
	expires->ivalue = sub->default_interval;
	pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);

	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    pjsip_msg_add_hdr(tdata->msg, 
			      pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
	}
	/* Send down to transaction. */
	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	/* Notify application if sub is terminated. */
	new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	sub_set_state(sub, new_state);
	if (new_state!=old_state && sub->cb.on_sub_terminated) {
	    pj_str_t reason = {"", 0};
	    if (reason_phrase.slen) reason = reason_phrase;
	    else reason = *pjsip_get_status_text(status);

	    sub->pending_tsx++;
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	    sub->pending_tsx--;
	}
    }

    pj_mutex_unlock(sub->mutex);

    /* Prefer to call log when we're not holding the mutex. */
    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", 
			 sub, state[sub->state].ptr,
			 (tdata ? tdata->obj_name : "null"), status));

    /* Check if application has requested deletion. */
    if (sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    }

}
Пример #11
0
/*
 * Stop subscription.
 */
PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
{
    pjsip_tx_data *tdata;
    const pjsip_route_hdr *route;
    pj_status_t status;

    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", 
			 sub, state[sub->state].ptr));

    /* Lock subscription. */
    pj_mutex_lock(sub->mutex);

    pj_assert(sub->role == PJSIP_ROLE_UAC);

    /* Kill refresh timer, if any. */
    if (sub->timer.id != 0) {
	sub->timer.id = 0;
	pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
    }

    /* Create request. */
    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 
						 &SUBSCRIBE,
						 sub->to->uri,
						 sub->from, sub->to, 
						 sub->contact, sub->call_id,
						 sub->cseq++,
						 NULL);

    if (!tdata) {
	pj_mutex_unlock(sub->mutex);
	return -1;
    }

    /* Add headers to request. */
    pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
    sub->uac_expires->ivalue = 0;
    pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));

    /* Add authentication. */
    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
			 sub->cred_cnt, sub->cred_info);


    /* Route set. */
    route = sub->route_set.next;
    while (route != &sub->route_set) {
	pj_list_insert_before( &tdata->msg->hdr,
			       pjsip_hdr_shallow_clone(tdata->pool, route));
	route = route->next;
    }

    /* Prevent timer from refreshing itself. */
    sub->default_interval = 0;

    /* Set state. */
    sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );

    /* Send the request. */
    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 
				       &on_subscribe_response);
    if (status == 0) {
	sub->pending_tsx++;
    }

    pj_mutex_unlock(sub->mutex);

    if (status != 0) {
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", 
			     sub, state[sub->state].ptr));
    }

    return status;
}
Пример #12
0
/*
 * Refresh subscription.
 */
static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
{
    pjsip_tx_data *tdata;
    pj_status_t status;
    const pjsip_route_hdr *route;

    pj_assert(sub->role == PJSIP_ROLE_UAC);
    pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
    if (sub->role != PJSIP_ROLE_UAC || 
	sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
    {
	return -1;
    }

    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", 
			 sub, state[sub->state].ptr));

    /* Create request. */
    tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 
						 &SUBSCRIBE,
						 sub->to->uri,
						 sub->from, sub->to, 
						 sub->contact, sub->call_id,
						 sub->cseq++,
						 NULL);

    if (!tdata) {
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
			     sub, state[sub->state].ptr));
	return -1;
    }

    pjsip_msg_add_hdr( tdata->msg, 
		       pjsip_hdr_shallow_clone(tdata->pool, sub->event));
    pjsip_msg_add_hdr( tdata->msg, 
		       pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
    pjsip_msg_add_hdr( tdata->msg, 
		       pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
    pjsip_msg_add_hdr( tdata->msg, 
		       pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));

    /* Authentication */
    pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
			 sub->cred_cnt, sub->cred_info);

    /* Route set. */
    route = sub->route_set.next;
    while (route != &sub->route_set) {
	pj_list_insert_before( &tdata->msg->hdr,
			       pjsip_hdr_shallow_clone(tdata->pool, route));
	route = route->next;
    }

    /* Send */
    status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 
				       &on_subscribe_response);
    if (status == 0) {
	sub->pending_tsx++;
    } else {
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", 
			     sub, state[sub->state].ptr));
    }

    return status;
}
Пример #13
0
/*
 * This callback called when we receive incoming NOTIFY request.
 */
static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
{
    pjsip_event_sub *sub;
    pjsip_tx_data *tdata;
    int status = 200;
    int old_state;
    pj_str_t reason = { NULL, 0 };
    pj_str_t reason_phrase = { NULL, 0 };
    int new_state = PJSIP_EVENT_SUB_STATE_NULL;

    /* Find subscription based on Call-ID and From tag. 
     * This will also automatically lock the subscription, if it's found.
     */
    sub = find_sub(rdata);
    if (!sub) {
	/* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
	 * Answer with 481 Subscription does not exist.
	 */
	PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
	status = 481;
	reason_phrase = pj_str("Subscription does not exist");

    } else {
	pj_assert(sub->role == PJSIP_ROLE_UAC);
	PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", 
			     sub, state[sub->state].ptr));

    }

    new_state = old_state = sub->state;

    /* RFC 3265: Section 3.2.1
     * Check that the Event header match the subscription. 
     */
    if (status == 200) {
	pjsip_event_hdr *hdr;
	pj_str_t hname = { "Event", 5 };

	hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
	if (!hdr) {
	    status = PJSIP_SC_BAD_REQUEST;
	    reason_phrase = pj_str("No Event header found");
	} else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
		   pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) 
	{
	    status = 481;
	    reason_phrase = pj_str("Subscription does not exist");
	}
    }

    /* Update subscription state and timer. */
    if (status == 200) {
	pjsip_sub_state_hdr *hdr;
	const pj_str_t hname = { "Subscription-State", 18 };
	const pj_str_t state_active = { "active", 6 },
		       state_pending = { "pending", 7},
		       state_terminated = { "terminated", 10 };

	hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
	if (!hdr) {
	    status = PJSIP_SC_BAD_REQUEST;
	    reason_phrase = pj_str("No Subscription-State header found");
	    goto process;
	} 

	/*
	 * Update subscription state.
	 */
	if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
		new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
	} else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
	    if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
		new_state = PJSIP_EVENT_SUB_STATE_PENDING;
	} else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
	    new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
	} else {
	    new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
	}

	reason = hdr->reason_param;

	if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
	    sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 
	{
	    sub_set_state(sub, new_state);
	    if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
		pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
	    } else {
		sub->state_str = state[new_state];
	    }
	}

	/*
	 * Update timeout timer in required, just in case notifier changed the 
         * expiration to shorter time.
	 * Section 3.2.2: the expires param can only shorten the interval.
	 */
	if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || 
	     sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) 
	{
	    pj_time_val now, new_expiry;

	    pj_gettimeofday(&now);
	    new_expiry.sec = now.sec + hdr->expires_param;
	    if (sub->timer.id==0 || 
		new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 
	    {
		update_next_refresh(sub, hdr->expires_param);
	    }
	}
    }

process:
    /* Note: here we sub MAY BE NULL! */

    /* Send response to NOTIFY */
    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
    if (tdata) {
	if (reason_phrase.slen)
	    tdata->msg->line.status.reason = reason_phrase;

	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    pjsip_hdr *hdr;
	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
	    pjsip_msg_add_hdr( tdata->msg, hdr);
	}

	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    /* Call NOTIFY callback, if any. */
    if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
	sub->pending_tsx++;
	(*sub->cb.on_received_notify)(sub, rdata);
	sub->pending_tsx--;
    }

    /* Check if subscription is terminated and call callback. */
    if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
	if (sub->cb.on_sub_terminated) {
	    sub->pending_tsx++;
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	    sub->pending_tsx--;
	}
    }

    /* Check if application has requested deletion. */
    if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
	pjsip_event_sub_destroy(sub);
    } else if (sub) {
	pj_mutex_unlock(sub->mutex);
    }
}
Пример #14
0
/* This function is called when we receive SUBSCRIBE request message for 
 * a new subscription.
 */
static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
{
    package *pkg;
    pj_pool_t *pool;
    pjsip_event_sub *sub = NULL;
    pj_str_t hname;
    int status = 200;
    pj_str_t reason = { NULL, 0 };
    pjsip_tx_data *tdata;
    pjsip_expires_hdr *expires;
    pjsip_accept_hdr *accept;
    pjsip_event_hdr *evhdr;

    /* Get the Event header. */
    hname = pj_str("Event");
    evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
    if (!evhdr) {
	status = 400;
	reason = pj_str("No Event header in request");
	goto send_response;
    }

    /* Find corresponding package. 
     * We don't lock the manager's mutex since we assume the package list
     * won't change once the application is running!
     */
    pkg = mgr.pkg_list.next;
    while (pkg != &mgr.pkg_list) {
	if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
	    break;
	pkg = pkg->next;
    }

    if (pkg == &mgr.pkg_list) {
	/* Event type is not supported by any packages! */
	status = 489;
	reason = pj_str("Bad Event");
	goto send_response;
    }

    /* First check that the Accept specification matches the 
     * package's Accept types.
     */
    accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
    if (accept) {
	unsigned i;
	pj_str_t *content_type = NULL;

	for (i=0; i<accept->count && !content_type; ++i) {
	    int j;
	    for (j=0; j<pkg->accept_cnt; ++j) {
		if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
		    content_type = &pkg->accept[j];
		    break;
		}
	    }
	}

	if (!content_type) {
	    status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
	    goto send_response;
	}
    }

    /* Check whether the package wants to accept the subscription. */
    pj_assert(pkg->cb.on_query_subscribe != NULL);
    (*pkg->cb.on_query_subscribe)(rdata, &status);
    if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
	goto send_response;

    /* Create new subscription record. */
    pool = pjsip_endpt_create_pool(tsx->endpt, "esub", 
				   SUB_POOL_SIZE, SUB_POOL_INC);
    if (!pool) {
	status = 500;
	goto send_response;
    }
    sub = pj_pool_calloc(pool, 1, sizeof(*sub));
    sub->pool = pool;
    sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
    if (!sub->mutex) {
	status = 500;
	goto send_response;
    }

    PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));

    /* Start locking mutex. */
    pj_mutex_lock(sub->mutex);

    /* Init UAS subscription */
    sub->endpt = tsx->endpt;
    sub->role = PJSIP_ROLE_UAS;
    sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
    sub->state_str = state[sub->state];
    pj_list_init(&sub->auth_sess);
    pj_list_init(&sub->route_set);
    sub->from = pjsip_hdr_clone(pool, rdata->to);
    pjsip_fromto_set_from(sub->from);
    if (sub->from->tag.slen == 0) {
	pj_create_unique_string(pool, &sub->from->tag);
	rdata->to->tag = sub->from->tag;
    }
    sub->to = pjsip_hdr_clone(pool, rdata->from);
    pjsip_fromto_set_to(sub->to);
    sub->contact = pjsip_contact_hdr_create(pool);
    sub->contact->uri = sub->from->uri;
    sub->call_id = pjsip_cid_hdr_create(pool);
    pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
    sub->cseq = pj_rand() % 0xFFFF;
    
    expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
    if (expires) {
	sub->default_interval = expires->ivalue;
	if (sub->default_interval > 0 && 
	    sub->default_interval < SECONDS_BEFORE_EXPIRY) 
	{
	    status = 423; /* Interval too short. */
	    goto send_response;
	}
    } else {
	sub->default_interval = 600;
    }

    /* Clone Event header. */
    sub->event = pjsip_hdr_clone(pool, evhdr);

    /* Register to hash table. */
    create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
			  &sub->from->tag);
    pj_mutex_lock(mgr.mutex);
    pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
    pj_mutex_unlock(mgr.mutex);

    /* Set timer where subscription will expire only when expires<>0. 
     * Subscriber may send new subscription with expires==0.
     */
    if (sub->default_interval != 0) {
	sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
    }

    /* Notify application. */
    if (pkg->cb.on_subscribe) {
	pjsip_event_sub_cb *cb = NULL;
	sub->pending_tsx++;
	(*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
	sub->pending_tsx--;
	if (cb == NULL)
	    pj_memset(&sub->cb, 0, sizeof(*cb));
	else
	    pj_memcpy(&sub->cb, cb, sizeof(*cb));
    }


send_response:
    PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", 
			  sub, state[sub->state].ptr, status));

    tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
    if (tdata) {
	if (reason.slen) {
	    /* Customize reason text. */
	    tdata->msg->line.status.reason = reason;
	}
	if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
	    /* Add Expires header. */
	    pjsip_expires_hdr *hdr;

	    hdr = pjsip_expires_hdr_create(tdata->pool);
	    hdr->ivalue = sub->default_interval;
	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
	}
	if (status == 423) {
	    /* Add Min-Expires header. */
	    pjsip_min_expires_hdr *hdr;

	    hdr = pjsip_min_expires_hdr_create(tdata->pool);
	    hdr->ivalue = SECONDS_BEFORE_EXPIRY;
	    pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
	}
	if (status == 489 || 
	    status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
	    PJSIP_IS_STATUS_IN_CLASS(status,200)) 
	{
	    /* Add Allow-Events header. */
	    pjsip_hdr *hdr;
	    hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
	    pjsip_msg_add_hdr(tdata->msg, hdr);

	    /* Should add Accept header?. */
	}

	pjsip_tsx_on_tx_msg(tsx, tdata);
    }

    /* If received new subscription with expires=0, terminate. */
    if (sub && sub->default_interval == 0) {
	pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
	if (sub->cb.on_sub_terminated) {
	    pj_str_t reason = { "timeout", 7 };
	    (*sub->cb.on_sub_terminated)(sub, &reason);
	}
    }

    if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
	if (sub && sub->mutex) {
	    pjsip_event_sub_destroy(sub);
	} else if (sub) {
	    pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
	}
    } else {
	pj_assert(status >= 200);
	pj_mutex_unlock(sub->mutex);
    }
}
Пример #15
0
/* 
 * Initialize new request message with authorization headers.
 * This function will put Authorization/Proxy-Authorization headers to the
 * outgoing request message. If caching is enabled (PJSIP_AUTH_HEADER_CACHING)
 * and the session has previously sent Authorization/Proxy-Authorization header
 * with the same method, then the same Authorization/Proxy-Authorization header
 * will be resent from the cache only if qop is not present. If the stack is 
 * configured to automatically generate next Authorization/Proxy-Authorization
 * headers (PJSIP_AUTH_AUTO_SEND_NEXT flag), then new Authorization/Proxy-
 * Authorization headers are calculated and generated when they are not present
 * in the case or if authorization session has qop.
 *
 * If both PJSIP_AUTH_HEADER_CACHING flag and PJSIP_AUTH_AUTO_SEND_NEXT flag
 * are not set, this function will do nothing. The stack then will only send
 * Authorization/Proxy-Authorization to respond 401/407 response.
 */
PJ_DEF(pj_status_t) pjsip_auth_init_req( pj_pool_t *sess_pool,
					 pjsip_tx_data *tdata,
					 pjsip_auth_session *sess_list,
					 int cred_count, 
					 const pjsip_cred_info cred_info[])
{
    pjsip_auth_session *sess;
    pjsip_method *method = &tdata->msg->line.req.method;

    pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG);

    if (!sess_list)
	return 0;

    sess = sess_list->next;
    while (sess != sess_list) {
	if (sess->qop_value == PJSIP_AUTH_QOP_NONE) {
#	    if (PJSIP_AUTH_HEADER_CACHING)
	    {
		pjsip_cached_auth_hdr *entry = sess->cached_hdr.next;
		while (entry != &sess->cached_hdr) {
		    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);
		    } else {
#			if (PJSIP_AUTH_AUTO_SEND_NEXT)
			{
			    new_auth_for_req( tdata, sess_pool, sess, 
					      cred_count, cred_info);
			}
#			else
			{
			    PJ_UNUSED_ARG(sess_pool);
			    PJ_UNUSED_ARG(cred_count);
			    PJ_UNUSED_ARG(cred_info);
			}
#			endif	/* PJSIP_AUTH_AUTO_SEND_NEXT */
		    }
		    entry = entry->next;
		}
	    }
#	    elif (PJSIP_AUTH_AUTO_SEND_NEXT)
	    {
		new_auth_for_req( tdata, sess_pool, sess, 
				  cred_count, cred_info);
	    }
#	    else
	    {
		PJ_UNUSED_ARG(sess_pool);
		PJ_UNUSED_ARG(cred_count);
		PJ_UNUSED_ARG(cred_info);
	    }
#	    endif   /* PJSIP_AUTH_HEADER_CACHING */

	} 
#	if (PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT)
	else if (sess->qop_value == PJSIP_AUTH_QOP_AUTH) {
	    /* For qop="auth", we have to re-create the authorization header. 
	     */
	    const pjsip_cred_info *cred;
	    pjsip_authorization_hdr *hauth;

	    cred = pjsip_auth_find_cred( cred_count, cred_info, 
					 &sess->realm, 
					 &sess->last_chal->scheme);
	    if (!cred) {
		sess = sess->next;
		continue;
	    }

	    hauth = pjsip_auth_respond( tdata->pool, sess->last_chal, 
					tdata->msg->line.req.uri, 
					cred,
					&tdata->msg->line.req.method,
					sess_pool, sess );
	    if (hauth) {
		pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)hauth);
	    }
	}
#	endif	/* PJSIP_AUTH_QOP_SUPPORT && PJSIP_AUTH_AUTO_SEND_NEXT */

	sess = sess->next;
    }
    return 0;
}