Beispiel #1
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);
    }
}
Beispiel #2
0
static void 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_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);
	}
    }

    /* 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;

	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 {
	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);

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

	    /* Schedule next registration */
	    if (regc->auto_reg && expiration > 0) {
		pj_time_val delay = { 0, 0};

		delay.sec = expiration - DELAY_BEFORE_REFRESH;
		if (regc->expires != PJSIP_REGC_EXPIRATION_NOT_SPECIFIED && 
		    delay.sec > (pj_int32_t)regc->expires) 
		{
		    delay.sec = regc->expires;
		}
		if (delay.sec < DELAY_BEFORE_REFRESH) 
		    delay.sec = DELAY_BEFORE_REFRESH;
		regc->timer.cb = &regc_refresh_timer_cb;
		regc->timer.id = REFRESH_TIMER;
		regc->timer.user_data = regc;
		pjsip_endpt_schedule_timer( regc->endpt, &regc->timer, &delay);
		pj_gettimeofday(&regc->last_reg);
		regc->next_reg = regc->last_reg;
		regc->next_reg.sec += delay.sec;
	    }

	} 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;

	/* 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 
			: pjsip_get_status_text(tsx->status_code)),
		      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);
    }
}