Пример #1
0
PJ_DECL(pj_status_t) vid_set_android_renderer(pjsua_call_id call_id,
		jobject window) {
	pj_status_t status = PJ_ENOTFOUND;
	pjsua_call *call;
	int i;

	if( !(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls) ){
			return PJ_ENOTFOUND;
	}

	PJ_LOG(4, (THIS_FILE, "Setup android renderer for call %d", call_id));
	PJSUA_LOCK();
	// Retrieve the stream
	if (pjsua_call_has_media(call_id)) {
		call = &pjsua_var.calls[call_id];
		for (i = 0; i < call->med_cnt; ++i) {
			pjsua_call_media *call_med = &call->media[i];
			vid_set_stream_window(call_med, PJMEDIA_DIR_RENDER, window);
			status = PJ_SUCCESS;
		}
	}

	PJSUA_UNLOCK();
	return status;
}
Пример #2
0
/*
 * Update buddy's presence.
 */
PJ_DEF(pj_status_t) pjsua_buddy_update_pres(pjsua_buddy_id buddy_id)
{
    pjsua_buddy *buddy;

    PJ_ASSERT_RETURN(buddy_id>=0 && 
			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_EINVAL);

    PJSUA_LOCK();

    buddy = &pjsua_var.buddy[buddy_id];

    /* Return error if buddy's presence monitoring is not enabled */
    if (!buddy->monitor) {
	PJSUA_UNLOCK();
	return PJ_EINVALIDOP;
    }

    /* Ignore if presence is already active for the buddy */
    if (buddy->sub) {
	PJSUA_UNLOCK();
	return PJ_SUCCESS;
    }

    /* Initiate presence subscription */
    subscribe_buddy_presence(buddy_id);

    PJSUA_UNLOCK();

    return PJ_SUCCESS;
}
Пример #3
0
/*
 * Delete buddy.
 */
PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_id)
{
    PJ_ASSERT_RETURN(buddy_id>=0 && 
			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_EINVAL);

    if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
	return PJ_SUCCESS;
    }

    /* Unsubscribe presence */
    pjsua_buddy_subscribe_pres(buddy_id, PJ_FALSE);

    PJSUA_LOCK();

    /* Not interested with further events for this buddy */
    if (pjsua_var.buddy[buddy_id].sub) {
	pjsip_evsub_set_mod_data(pjsua_var.buddy[buddy_id].sub, 
				 pjsua_var.mod.id, NULL);
    }

    /* Remove buddy */
    pjsua_var.buddy[buddy_id].uri.slen = 0;
    pjsua_var.buddy_cnt--;

    /* Reset buddy struct */
    reset_buddy(buddy_id);

    PJSUA_UNLOCK();
    return PJ_SUCCESS;
}
Пример #4
0
/* Callback called when we receive NOTIFY */
static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub, 
				     pjsip_rx_data *rdata,
				     int *p_st_code,
				     pj_str_t **p_st_text,
				     pjsip_hdr *res_hdr,
				     pjsip_msg_body **p_body)
{
    pjsua_buddy *buddy;

    PJSUA_LOCK();

    buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
    if (buddy) {
	/* Update our info. */
	pjsip_pres_get_status(sub, &buddy->status);
    }

    /* The default is to send 200 response to NOTIFY.
     * Just leave it there..
     */
    PJ_UNUSED_ARG(rdata);
    PJ_UNUSED_ARG(p_st_code);
    PJ_UNUSED_ARG(p_st_text);
    PJ_UNUSED_ARG(res_hdr);
    PJ_UNUSED_ARG(p_body);

    PJSUA_UNLOCK();
}
Пример #5
0
PJ_DECL(pj_status_t) vid_set_android_capturer(jobject window) {
    unsigned ci, i, count;
    pj_status_t status = PJ_ENOTFOUND;
    pjsua_call *call;
    pjsua_call_id calls_id[PJSUA_MAX_ACC];

    count = PJ_ARRAY_SIZE(calls_id);
    status = pjsua_enum_calls(calls_id, &count);
    if(status != PJ_SUCCESS){
        return status;
    }

    PJ_LOG(4, (THIS_FILE, "Setup android capturer for all calls"));
    PJSUA_LOCK();
    for(ci = 0; ci < count; ++ci){
        pjsua_call_id call_id = calls_id[ci];
        if(pjsua_call_is_active(call_id) && pjsua_call_has_media(call_id)){
            call = &pjsua_var.calls[call_id];
            for (i = 0; i < call->med_cnt; ++i) {
                pjsua_call_media *call_med = &call->media[i];
                vid_set_stream_window(call_med, PJMEDIA_DIR_CAPTURE, window);
                status = PJ_SUCCESS;
            }
        }
    }

    PJSUA_UNLOCK();
    return status;
}
Пример #6
0
PJ_DECL(zrtp_state_info) jzrtp_getInfoFromCall(pjsua_call_id call_id){
	PJSUA_LOCK();
	struct jzrtp_allContext ctxt = jzrtp_getContext(call_id);
	zrtp_state_info info = jzrtp_getInfoFromContext(ctxt);
	PJSUA_UNLOCK();
	return info;
}
Пример #7
0
/* Callback called when *server* subscription state has changed. */
static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
{
    pjsua_srv_pres *uapres;

    PJ_UNUSED_ARG(event);

    PJSUA_LOCK();

    uapres = (pjsua_srv_pres*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
    if (uapres) {
	pjsip_evsub_state state;

	PJ_LOG(4,(THIS_FILE, "Server subscription to %s is %s",
		  uapres->remote, pjsip_evsub_get_state_name(sub)));

	state = pjsip_evsub_get_state(sub);

	if (pjsua_var.ua_cfg.cb.on_srv_subscribe_state) {
	    pj_str_t from;

	    from = uapres->dlg->remote.info_str;
	    (*pjsua_var.ua_cfg.cb.on_srv_subscribe_state)(uapres->acc_id, 
							  uapres, &from,
							  state, event);
	}

	if (state == PJSIP_EVSUB_STATE_TERMINATED) {
	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
	    pj_list_erase(uapres);
	}
    }

    PJSUA_UNLOCK();
}
Пример #8
0
/*
 * Add a new account to pjsua.
 */
PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg,
				   pj_bool_t is_default,
				   pjsua_acc_id *p_acc_id)
{
    pjsua_acc *acc;
    unsigned id;
    pj_status_t status;

    PJ_ASSERT_RETURN(pjsua_var.acc_cnt < PJ_ARRAY_SIZE(pjsua_var.acc),
		     PJ_ETOOMANY);

    /* Must have a transport */
    PJ_ASSERT_RETURN(pjsua_var.tpdata[0].data.ptr != NULL, PJ_EINVALIDOP);

    PJSUA_LOCK();

    /* Find empty account id. */
    for (id=0; id < PJ_ARRAY_SIZE(pjsua_var.acc); ++id) {
	if (pjsua_var.acc[id].valid == PJ_FALSE)
	    break;
    }

    /* Expect to find a slot */
    PJ_ASSERT_ON_FAIL(	id < PJ_ARRAY_SIZE(pjsua_var.acc), 
			{PJSUA_UNLOCK(); return PJ_EBUG;});
Пример #9
0
zrtp_state_info jzrtp_getInfoFromTransport(pjmedia_transport* tp){
	PJSUA_LOCK();
	struct jzrtp_allContext ctxt;
	ctxt.zrtpContext = pjmedia_transport_zrtp_getZrtpContext(tp);
	ctxt.cbUserData = (zrtp_cb_user_data*) pjmedia_transport_zrtp_getUserData(tp);
	zrtp_state_info info =  jzrtp_getInfoFromContext(ctxt);
	PJSUA_UNLOCK();
	return info;
}
Пример #10
0
/* Callback when transaction state has changed. */
static void pjsua_evsub_on_tsx_state(pjsip_evsub *sub, 
				     pjsip_transaction *tsx,
				     pjsip_event *event)
{
    pjsua_buddy *buddy;
    pjsip_contact_hdr *contact_hdr;

    PJSUA_LOCK();

    buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
    if (!buddy) {
	PJSUA_UNLOCK();
	return;
    }

    /* We only use this to update buddy's Contact, when it's not
     * set.
     */
    if (buddy->contact.slen != 0) {
	/* Contact already set */
	PJSUA_UNLOCK();
	return;
    }
    
    /* Only care about 2xx response to outgoing SUBSCRIBE */
    if (tsx->status_code/100 != 2 ||
	tsx->role != PJSIP_UAC_ROLE ||
	event->type != PJSIP_EVENT_RX_MSG || 
	pjsip_method_cmp(&tsx->method, pjsip_get_subscribe_method())!=0)
    {
	PJSUA_UNLOCK();
	return;
    }

    /* Find contact header. */
    contact_hdr = (pjsip_contact_hdr*)
		  pjsip_msg_find_hdr(event->body.rx_msg.rdata->msg_info.msg,
				     PJSIP_H_CONTACT, NULL);
    if (!contact_hdr) {
	PJSUA_UNLOCK();
	return;
    }

    buddy->contact.ptr = (char*)
			 pj_pool_alloc(buddy->pool, PJSIP_MAX_URL_SIZE);
    buddy->contact.slen = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,
					   contact_hdr->uri,
					   buddy->contact.ptr, 
					   PJSIP_MAX_URL_SIZE);
    if (buddy->contact.slen < 0)
	buddy->contact.slen = 0;

    PJSUA_UNLOCK();
}
Пример #11
0
/**
 * Is call using a secure transport method (No (0) / TLS (1) / SIPS (2))
 */
PJ_DECL(int) call_secure_sig_level(pjsua_call_id call_id) {
    int value = 0;
    pjsua_call *call;
    PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
            value);

    PJSUA_LOCK();
    call = &pjsua_var.calls[call_id];
    value = call->secure_level;
    PJSUA_UNLOCK();

    return value;

}
Пример #12
0
/*
 * Set the user data associated with the buddy object.
 */
PJ_DEF(pj_status_t) pjsua_buddy_set_user_data( pjsua_buddy_id buddy_id,
					       void *user_data)
{
    PJ_ASSERT_RETURN(buddy_id>=0 && 
		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_EINVAL);

    PJSUA_LOCK();

    pjsua_var.buddy[buddy_id].user_data = user_data;

    PJSUA_UNLOCK();

    return PJ_SUCCESS;
}
Пример #13
0
/*
 * Get the user data associated with the budy object.
 */
PJ_DEF(void*) pjsua_buddy_get_user_data(pjsua_buddy_id buddy_id)
{
    void *user_data;

    PJ_ASSERT_RETURN(buddy_id>=0 && 
		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
		     NULL);

    PJSUA_LOCK();

    user_data = pjsua_var.buddy[buddy_id].user_data;

    PJSUA_UNLOCK();

    return user_data;
}
Пример #14
0
PJ_DECL(pj_status_t) update_transport(const pj_str_t *new_ip_addr) {
	PJSUA_LOCK();

	PJ_LOG(4, (THIS_FILE,"update_transport to addr = %s", new_ip_addr->ptr));
	// No need ot check thread cause csipsimple use handler thread

	/*
	 pjsua_transport_config cfg;
	 pjsua_transport_config_default(&cfg);
	 cfg.port = 0;

	 pjsua_media_transports_create(&cfg);
	 */
	update_active_calls(new_ip_addr);

	PJSUA_UNLOCK();
	return PJ_SUCCESS;
}
Пример #15
0
/* Callback called when *client* subscription state has changed. */
static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
{
    pjsua_buddy *buddy;

    PJ_UNUSED_ARG(event);

    PJSUA_LOCK();

    buddy = (pjsua_buddy*) pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id);
    if (buddy) {
	PJ_LOG(4,(THIS_FILE, 
		  "Presence subscription to %.*s is %s",
		  (int)pjsua_var.buddy[buddy->index].uri.slen,
		  pjsua_var.buddy[buddy->index].uri.ptr, 
		  pjsip_evsub_get_state_name(sub)));

	if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
	    if (buddy->term_reason.ptr == NULL) {
		buddy->term_reason.ptr = (char*) 
					 pj_pool_alloc(buddy->pool,
					   PJSUA_BUDDY_SUB_TERM_REASON_LEN);
	    }
	    pj_strncpy(&buddy->term_reason, 
		       pjsip_evsub_get_termination_reason(sub), 
		       PJSUA_BUDDY_SUB_TERM_REASON_LEN);
	} else {
	    buddy->term_reason.slen = 0;
	}

	/* Call callback */
	if (pjsua_var.ua_cfg.cb.on_buddy_state)
	    (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index);

	/* Clear subscription */
	if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
	    buddy->sub = NULL;
	    buddy->status.info_cnt = 0;
	    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL);
	}
    }

    PJSUA_UNLOCK();
}
Пример #16
0
/*
 * Enable/disable buddy's presence monitoring.
 */
PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id buddy_id,
						pj_bool_t subscribe)
{
    pjsua_buddy *buddy;

    PJ_ASSERT_RETURN(buddy_id>=0 && 
			buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_EINVAL);

    PJSUA_LOCK();

    buddy = &pjsua_var.buddy[buddy_id];
    buddy->monitor = subscribe;

    PJSUA_UNLOCK();

    pjsua_pres_refresh();

    return PJ_SUCCESS;
}
Пример #17
0
/*
 * Enum buddy IDs.
 */
PJ_DEF(pj_status_t) pjsua_enum_buddies( pjsua_buddy_id ids[],
					unsigned *count)
{
    unsigned i, c;

    PJ_ASSERT_RETURN(ids && count, PJ_EINVAL);

    PJSUA_LOCK();

    for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
	if (!pjsua_var.buddy[i].uri.slen)
	    continue;
	ids[c] = i;
	++c;
    }

    *count = c;

    PJSUA_UNLOCK();

    return PJ_SUCCESS;
}
Пример #18
0
/*
 * Destroy recorder (this will complete recording).
 */
PJ_DEF(pj_status_t) pjsua_sxs_switcher_destroy(pjsua_recorder_id id)
{
    PJ_ASSERT_RETURN(id>=0 && id<(int)PJ_ARRAY_SIZE(pjsua_var.recorder), 
                     PJ_EINVAL);
    PJ_ASSERT_RETURN(pjsua_var.recorder[id].port != NULL, PJ_EINVAL);

    PJSUA_LOCK();

    if (pjsua_var.recorder[id].port) {
        pjsua_conf_remove_port(pjsua_var.recorder[id].slot);
        pjmedia_port_destroy(pjsua_var.recorder[id].port);
        pjsua_var.recorder[id].port = NULL;
        pjsua_var.recorder[id].slot = 0xFFFF;
        pj_pool_release(pjsua_var.recorder[id].pool);
        pjsua_var.recorder[id].pool = NULL;
        pjsua_var.rec_cnt--;
    }

    PJSUA_UNLOCK();

    return PJ_SUCCESS;
}
Пример #19
0
/**
 * Is call using a secure RTP method (SRTP/ZRTP)
 */
PJ_DECL(pj_str_t) call_secure_info(pjsua_call_id call_id) {

	pjsua_call *call;
	pj_status_t status;
	unsigned i;
	pjmedia_transport_info tp_info;

	pj_str_t result = pj_str("");

	PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
			result);

	PJSUA_LOCK();

	if (pjsua_call_has_media(call_id)) {
		call = &pjsua_var.calls[call_id];
		for (i = 0; i < call->med_cnt; ++i) {
			pjsua_call_media *call_med = &call->media[i];
			PJ_LOG(4, (THIS_FILE, "Get secure for media type %d", call_med->type));
			if (call_med->tp && call_med->type == PJMEDIA_TYPE_AUDIO) {
				pjmedia_transport_info tp_info;

				pjmedia_transport_info_init(&tp_info);
				pjmedia_transport_get_info(call_med->tp, &tp_info);
				if (tp_info.specific_info_cnt > 0) {
					unsigned j;
					for (j = 0; j < tp_info.specific_info_cnt; ++j) {
						if (tp_info.spc_info[j].type
								== PJMEDIA_TRANSPORT_TYPE_SRTP) {
							pjmedia_srtp_info *srtp_info =
									(pjmedia_srtp_info*) tp_info.spc_info[j].buffer;
							if (srtp_info->active) {
								result = pj_str("SRTP");
								break;
							}
						}

#if defined(PJMEDIA_HAS_ZRTP) && PJMEDIA_HAS_ZRTP!=0
						else if (tp_info.spc_info[j].type
								== PJMEDIA_TRANSPORT_TYPE_ZRTP) {
							zrtp_state_info info = jzrtp_getInfoFromTransport(call_med->tp);
							if(info.secure){
								char msg[512];
								PJ_LOG(4, (THIS_FILE, "ZRTP :: V %d", info.sas_verified));
								PJ_LOG(4, (THIS_FILE, "ZRTP :: S L %d", info.sas.slen));
								PJ_LOG(4, (THIS_FILE, "ZRTP :: C L %d", info.cipher.slen));

								pj_ansi_snprintf(msg, sizeof(msg), "ZRTP - %s\n%.*s\n%.*s",
										info.sas_verified ? "Verified": "Not verified",
										info.sas.slen, info.sas.ptr,
										info.cipher.slen, info.cipher.ptr);

								pj_strdup2_with_null(css_var.pool, &result, msg);
								break;
							}
						}
#endif
					}
				}
			}
		}
	}

	PJSUA_UNLOCK();

	return result;
}
Пример #20
0
/*
 * Get detailed buddy info.
 */
PJ_DEF(pj_status_t) pjsua_buddy_get_info( pjsua_buddy_id buddy_id,
					  pjsua_buddy_info *info)
{
    unsigned total=0;
    pjsua_buddy *buddy;

    PJ_ASSERT_RETURN(buddy_id>=0 && 
		       buddy_id<(int)PJ_ARRAY_SIZE(pjsua_var.buddy), 
		     PJ_EINVAL);

    PJSUA_LOCK();

    pj_bzero(info, sizeof(pjsua_buddy_info));

    buddy = &pjsua_var.buddy[buddy_id];
    info->id = buddy->index;
    if (pjsua_var.buddy[buddy_id].uri.slen == 0) {
	PJSUA_UNLOCK();
	return PJ_SUCCESS;
    }

    /* uri */
    info->uri.ptr = info->buf_ + total;
    pj_strncpy(&info->uri, &buddy->uri, sizeof(info->buf_)-total);
    total += info->uri.slen;

    /* contact */
    info->contact.ptr = info->buf_ + total;
    pj_strncpy(&info->contact, &buddy->contact, sizeof(info->buf_)-total);
    total += info->contact.slen;

    /* status and status text */    
    if (buddy->sub == NULL || buddy->status.info_cnt==0) {
	info->status = PJSUA_BUDDY_STATUS_UNKNOWN;
	info->status_text = pj_str("?");
    } else if (pjsua_var.buddy[buddy_id].status.info[0].basic_open) {
	info->status = PJSUA_BUDDY_STATUS_ONLINE;

	/* copy RPID information */
	info->rpid = buddy->status.info[0].rpid;

	if (info->rpid.note.slen)
	    info->status_text = info->rpid.note;
	else
	    info->status_text = pj_str("Online");

    } else {
	info->status = PJSUA_BUDDY_STATUS_OFFLINE;
	info->status_text = pj_str("Offline");
    }

    /* monitor pres */
    info->monitor_pres = buddy->monitor;

    /* subscription state and termination reason */
    if (buddy->sub) {
	info->sub_state = pjsip_evsub_get_state(buddy->sub);
	if (info->sub_state == PJSIP_EVSUB_STATE_TERMINATED &&
	    total < sizeof(info->buf_)) 
	{
	    info->sub_term_reason.ptr = info->buf_ + total;
	    pj_strncpy(&info->sub_term_reason,
		       pjsip_evsub_get_termination_reason(buddy->sub),
		       sizeof(info->buf_) - total);
	    total += info->sub_term_reason.slen;
	} else {
	    info->sub_term_reason = pj_str("");
	}
    } else if (total < sizeof(info->buf_)) {
	info->sub_term_reason.ptr = info->buf_ + total;
	pj_strncpy(&info->sub_term_reason, &buddy->term_reason,
		   sizeof(info->buf_) - total);
	total += info->sub_term_reason.slen;
    } else {
	info->sub_term_reason = pj_str("");
    }

    PJSUA_UNLOCK();
    return PJ_SUCCESS;
}
Пример #21
0
/*
 * Add new buddy.
 */
PJ_DEF(pj_status_t) pjsua_buddy_add( const pjsua_buddy_config *cfg,
				     pjsua_buddy_id *p_buddy_id)
{
    pjsip_name_addr *url;
    pjsua_buddy *buddy;
    pjsip_sip_uri *sip_uri;
    int index;
    pj_str_t tmp;

    PJ_ASSERT_RETURN(pjsua_var.buddy_cnt <= 
			PJ_ARRAY_SIZE(pjsua_var.buddy),
		     PJ_ETOOMANY);

    PJSUA_LOCK();

    /* Find empty slot */
    for (index=0; index<(int)PJ_ARRAY_SIZE(pjsua_var.buddy); ++index) {
	if (pjsua_var.buddy[index].uri.slen == 0)
	    break;
    }

    /* Expect to find an empty slot */
    if (index == PJ_ARRAY_SIZE(pjsua_var.buddy)) {
	PJSUA_UNLOCK();
	/* This shouldn't happen */
	pj_assert(!"index < PJ_ARRAY_SIZE(pjsua_var.buddy)");
	return PJ_ETOOMANY;
    }

    buddy = &pjsua_var.buddy[index];

    /* Create pool for this buddy */
    if (buddy->pool) {
	pj_pool_reset(buddy->pool);
    } else {
	char name[PJ_MAX_OBJ_NAME];
	pj_ansi_snprintf(name, sizeof(name), "buddy%03d", index);
	buddy->pool = pjsua_pool_create(name, 512, 256);
    }

    /* Init buffers for presence subscription status */
    buddy->term_reason.ptr = (char*) 
			     pj_pool_alloc(buddy->pool, 
					   PJSUA_BUDDY_SUB_TERM_REASON_LEN);

    /* Get name and display name for buddy */
    pj_strdup_with_null(buddy->pool, &tmp, &cfg->uri);
    url = (pjsip_name_addr*)pjsip_parse_uri(buddy->pool, tmp.ptr, tmp.slen,
					    PJSIP_PARSE_URI_AS_NAMEADDR);

    if (url == NULL) {
	pjsua_perror(THIS_FILE, "Unable to add buddy", PJSIP_EINVALIDURI);
	pj_pool_release(buddy->pool);
	buddy->pool = NULL;
	PJSUA_UNLOCK();
	return PJSIP_EINVALIDURI;
    }

    /* Only support SIP schemes */
    if (!PJSIP_URI_SCHEME_IS_SIP(url) && !PJSIP_URI_SCHEME_IS_SIPS(url)) {
	pj_pool_release(buddy->pool);
	buddy->pool = NULL;
	PJSUA_UNLOCK();
	return PJSIP_EINVALIDSCHEME;
    }

    /* Reset buddy, to make sure everything is cleared with default
     * values
     */
    reset_buddy(index);

    /* Save URI */
    pjsua_var.buddy[index].uri = tmp;

    sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(url->uri);
    pjsua_var.buddy[index].name = sip_uri->user;
    pjsua_var.buddy[index].display = url->display;
    pjsua_var.buddy[index].host = sip_uri->host;
    pjsua_var.buddy[index].port = sip_uri->port;
    pjsua_var.buddy[index].monitor = cfg->subscribe;
    if (pjsua_var.buddy[index].port == 0)
	pjsua_var.buddy[index].port = 5060;

    /* Save user data */
    pjsua_var.buddy[index].user_data = (void*)cfg->user_data;

    if (p_buddy_id)
	*p_buddy_id = index;

    pjsua_var.buddy_cnt++;

    PJSUA_UNLOCK();

    pjsua_buddy_subscribe_pres(index, cfg->subscribe);

    return PJ_SUCCESS;
}
Пример #22
0
/*
 * Create a file recorder, and automatically connect this recorder to
 * the conference bridge.
 */
PJ_DEF(pj_status_t) pjsua_sxs_switcher_create( // const pj_str_t *filename,
                                              int direct_type,
                                              unsigned enc_type,
                                              void *enc_param,
                                              unsigned options,
                                              pjsua_recorder_id *p_id)
{
    enum Format
    {
        FMT_UNKNOWN,
        FMT_WAV,
        FMT_MP3,
    };
    unsigned slot, file_id;
    char path[PJ_MAXPATH];
    pj_str_t ext;
    int file_format;
    pjmedia_port *port;
    pj_status_t status;

    /* Filename must present */
    // PJ_ASSERT_RETURN(filename != NULL, PJ_EINVAL);

    /* Don't support encoding type at present */
    PJ_ASSERT_RETURN(enc_type == 0, PJ_EINVAL);

    if (pjsua_var.rec_cnt >= PJ_ARRAY_SIZE(pjsua_var.recorder))
        return PJ_ETOOMANY;

    file_format = FMT_WAV;

    PJSUA_LOCK();

    for (file_id=0; file_id<PJ_ARRAY_SIZE(pjsua_var.recorder); ++file_id) {
        if (pjsua_var.recorder[file_id].port == NULL)
            break;
    }

    if (file_id == PJ_ARRAY_SIZE(pjsua_var.recorder)) {
        /* This is unexpected */
        PJSUA_UNLOCK();
        pj_assert(0);
        return PJ_EBUG;
    }

    if (file_format == FMT_WAV) {
        status = pjmedia_sxs_port_server_create(
                                                pjsua_var.media_cfg.clock_rate, 
                                                pjsua_var.mconf_cfg.channel_count,
                                                pjsua_var.mconf_cfg.samples_per_frame,
                                                pjsua_var.mconf_cfg.bits_per_sample, 
                                                options, 0, &port);
        fprintf(stderr, "samples_per_frame=%d, bits_per_samples=%d\n",
                pjsua_var.mconf_cfg.samples_per_frame,
                pjsua_var.mconf_cfg.bits_per_sample);
    } else {
        PJ_UNUSED_ARG(enc_param);
        port = NULL;
        status = PJ_ENOTSUP;
    }

    if (status != PJ_SUCCESS) {
        PJSUA_UNLOCK();
        pjsua_perror(THIS_FILE, "Unable to open file for recording", status);
        return status;
    }

    pjmedia_sxs_port_outgoing_server_get_port(port, 0);
    if (direct_type == SUA_SWITCHER_SERVER) {
    } else {
    }

    // const pj_str_t filename = pj_str(path);
    assert(g_param->pool);
    status = pjmedia_conf_add_port(pjsua_var.mconf, g_param->pool, 
                                   port, 0, &slot);

    if (status != PJ_SUCCESS) {
        pjmedia_port_destroy(port);
        PJSUA_UNLOCK();
        return status;
    }

    pjsua_var.recorder[file_id].port = port;
    pjsua_var.recorder[file_id].slot = slot;

    if (p_id) *p_id = file_id;

    ++pjsua_var.rec_cnt;

    PJSUA_UNLOCK();
    return PJ_SUCCESS;
}
Пример #23
0
/*
 * This is an internal function to find the most appropriate account to be
 * used to handle incoming calls.
 */
void on_acc_find_for_incoming_wrapper(const pjsip_rx_data *rdata, pjsua_acc_id* out_acc_id)
{
    pjsip_uri *uri;
    pjsip_sip_uri *sip_uri;
    unsigned i;
    int current_matching_score = 0;
    int matching_scores[PJSUA_MAX_ACC];
    pjsua_acc_id best_matching = pjsua_var.default_acc;

    /* Check that there's at least one account configured */
    PJ_ASSERT_RETURN(pjsua_var.acc_cnt!=0, pjsua_var.default_acc);

    uri = rdata->msg_info.to->uri;

    /* Just return if To URI is not SIP: */
    if (!PJSIP_URI_SCHEME_IS_SIP(uri) &&
    !PJSIP_URI_SCHEME_IS_SIPS(uri))
    {
    return;
    }


    PJSUA_LOCK();

    sip_uri = (pjsip_sip_uri*)pjsip_uri_get_uri(uri);

    /* Find account which has matching username and domain. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid && pj_stricmp(&acc->user_part, &sip_uri->user)==0 &&
        pj_stricmp(&acc->srv_domain, &sip_uri->host)==0)
    {
        /* Match ! */
        PJSUA_UNLOCK();
        *out_acc_id = acc_id;
        return;
    }
    }

    /* No exact matching, try fuzzy matching */
    pj_bzero(matching_scores, sizeof(matching_scores));

    /* No matching account, try match domain part only. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid && pj_stricmp(&acc->srv_domain, &sip_uri->host)==0) {
        /* Match ! */
        /* We apply 100 weight if account has reg uri
         * Because in pragmatic case we are more looking
         * for these one than for the local acc
         */
        matching_scores[i] += (acc->cfg.reg_uri.slen > 0) ? (300 * sip_uri->host.slen) : 1;
    }
    }

    /* No matching account, try match user part (and transport type) only. */
    for (i=0; i < pjsua_var.acc_cnt; ++i) {
    unsigned acc_id = pjsua_var.acc_ids[i];
    pjsua_acc *acc = &pjsua_var.acc[acc_id];

    if (acc->valid) {
        /* We apply 100 weight if account has reg uri
         * Because in pragmatic case we are more looking
         * for these one than for the local acc
         */
        unsigned weight = (acc->cfg.reg_uri.slen > 0) ? 100 : 1;

        if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
        pjsip_transport_type_e type;
        type = pjsip_transport_get_type_from_name(&sip_uri->transport_param);
        if (type == PJSIP_TRANSPORT_UNSPECIFIED)
            type = PJSIP_TRANSPORT_UDP;

        if (pjsua_var.tpdata[acc->cfg.transport_id].type != type)
            continue;
        }
        /* Match ! */
        matching_scores[i] += (max_common_substr_len(&acc->user_part, &sip_uri->user) * weight);
    }
    }

    /* Still no match, use default account */
    PJSUA_UNLOCK();
    for(i=0; i<pjsua_var.acc_cnt; i++) {
        if(current_matching_score < matching_scores[i])
        {
            best_matching = pjsua_var.acc_ids[i];
            current_matching_score = matching_scores[i];
        }
    }
    *out_acc_id = best_matching;
}
Пример #24
0
/**
 * Is call using a secure RTP method (SRTP/ZRTP)
 */
PJ_DECL(pj_str_t) call_secure_info(pjsua_call_id call_id) {

	pjsua_call *call;
	pjsip_dialog *dlg;
	pj_status_t status;
	unsigned i;
	pjmedia_transport_info tp_info;

	pj_str_t result = pj_str("");
	PJ_LOG(3, (THIS_FILE, "Get call secure info..."));

	PJ_ASSERT_RETURN(call_id>=0 && call_id<(int)pjsua_var.ua_cfg.max_calls,
			result);

	/* Use PJSUA_LOCK() instead of acquire_call():
	 *  https://trac.pjsip.org/repos/ticket/1371
	 */
	PJSUA_LOCK();

	if (pjsua_call_has_media(call_id)) {
		call = &pjsua_var.calls[call_id];
		for (i = 0; i < call->med_cnt; ++i) {
			pjsua_call_media *call_med = &call->media[i];
			PJ_LOG(4, (THIS_FILE, "Get secure for media type %d", call_med->type));
			/* Get and ICE SRTP status */
			if (call_med->tp && call_med->type == PJMEDIA_TYPE_AUDIO) {
				pjmedia_transport_info tp_info;

				pjmedia_transport_info_init(&tp_info);
				pjmedia_transport_get_info(call_med->tp, &tp_info);
				if (tp_info.specific_info_cnt > 0) {
					unsigned j;
					for (j = 0; j < tp_info.specific_info_cnt; ++j) {
						if (tp_info.spc_info[j].type
								== PJMEDIA_TRANSPORT_TYPE_SRTP) {
							pjmedia_srtp_info *srtp_info =
									(pjmedia_srtp_info*) tp_info.spc_info[j].buffer;
							if (srtp_info->active) {
								result = pj_str("SRTP");
								break;
							}
						}

#if defined(PJMEDIA_HAS_ZRTP) && PJMEDIA_HAS_ZRTP!=0
						else if (tp_info.spc_info[j].type
								== PJMEDIA_TRANSPORT_TYPE_ZRTP) {
							pjmedia_zrtp_info *zrtp_info =
									(pjmedia_zrtp_info*) tp_info.spc_info[j].buffer;

							//	if(zrtp_info->active){
							result = jzrtp_getInfo(call_med->tp);
							break;
							//	}
						}
#endif
					}
				}
			}
		}
	}

	PJSUA_UNLOCK();

	return result;
}
Пример #25
0
/*
 * Send NOTIFY.
 */
PJ_DEF(pj_status_t) pjsua_pres_notify( pjsua_acc_id acc_id,
				       pjsua_srv_pres *srv_pres,
				       pjsip_evsub_state ev_state,
				       const pj_str_t *state_str,
				       const pj_str_t *reason,
				       pj_bool_t with_body,
				       const pjsua_msg_data *msg_data)
{
    pjsua_acc *acc;
    pjsip_pres_status pres_status;
    pjsua_buddy_id buddy_id;
    pjsip_tx_data *tdata;
    pj_status_t status;

    /* Check parameters */
    PJ_ASSERT_RETURN(acc_id!=-1 && srv_pres, PJ_EINVAL);

    /* Check that account ID is valid */
    PJ_ASSERT_RETURN(acc_id>=0 && acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc),
		     PJ_EINVAL);
    /* Check that account is valid */
    PJ_ASSERT_RETURN(pjsua_var.acc[acc_id].valid, PJ_EINVALIDOP);

    PJSUA_LOCK();

    acc = &pjsua_var.acc[acc_id];

    /* Check that the server presence subscription is still valid */
    if (pj_list_find_node(&acc->pres_srv_list, srv_pres) == NULL) {
	/* Subscription has been terminated */
	PJSUA_UNLOCK();
	return PJ_EINVALIDOP;
    }

    /* Set our online status: */
    pj_bzero(&pres_status, sizeof(pres_status));
    pres_status.info_cnt = 1;
    pres_status.info[0].basic_open = acc->online_status;
    pres_status.info[0].id = acc->cfg.pidf_tuple_id;
    //Both pjsua_var.local_uri and pjsua_var.contact_uri are enclosed in "<" and ">"
    //causing XML parsing to fail.
    //pres_status.info[0].contact = pjsua_var.local_uri;
    /* add RPID information */
    pj_memcpy(&pres_status.info[0].rpid, &acc->rpid, 
	      sizeof(pjrpid_element));

    pjsip_pres_set_status(srv_pres->sub, &pres_status);

    /* Check expires value. If it's zero, send our presense state but
     * set subscription state to TERMINATED.
     */
    if (srv_pres->expires == 0)
	ev_state = PJSIP_EVSUB_STATE_TERMINATED;

    /* Create and send the NOTIFY to active subscription: */
    status = pjsip_pres_notify(srv_pres->sub, ev_state, state_str, 
			       reason, &tdata);
    if (status == PJ_SUCCESS) {
	/* Force removal of message body if msg_body==FALSE */
	if (!with_body) {
	    tdata->msg->body = NULL;
	}
	pjsua_process_msg_data(tdata, msg_data);
	status = pjsip_pres_send_request( srv_pres->sub, tdata);
    }

    if (status != PJ_SUCCESS) {
	pjsua_perror(THIS_FILE, "Unable to create/send NOTIFY", 
		     status);
	pj_list_erase(srv_pres);
	pjsip_pres_terminate(srv_pres->sub, PJ_FALSE);
	PJSUA_UNLOCK();
	return status;
    }


    /* Subscribe to buddy's presence if we're not subscribed */
    buddy_id = pjsua_find_buddy(srv_pres->dlg->remote.info->uri);
    if (buddy_id != PJSUA_INVALID_ID) {
	pjsua_buddy *b = &pjsua_var.buddy[buddy_id];
	if (b->monitor && b->sub == NULL) {
	    PJ_LOG(4,(THIS_FILE, "Received SUBSCRIBE from buddy %d, "
		      "activating outgoing subscription", buddy_id));
	    subscribe_buddy_presence(buddy_id);
	}
    }

    PJSUA_UNLOCK();

    return PJ_SUCCESS;
}
Пример #26
0
/* This is called when request is received. 
 * We need to check for incoming SUBSCRIBE request.
 */
static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
{
    int acc_id;
    pjsua_acc *acc;
    pj_str_t contact;
    pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
    pjsua_srv_pres *uapres;
    pjsip_evsub *sub;
    pjsip_evsub_user pres_cb;
    pjsip_dialog *dlg;
    pjsip_status_code st_code;
    pj_str_t reason;
    pjsip_expires_hdr *expires_hdr;
    pjsua_msg_data msg_data;
    pj_status_t status;

    if (pjsip_method_cmp(req_method, pjsip_get_subscribe_method()) != 0)
	return PJ_FALSE;

    /* Incoming SUBSCRIBE: */

    PJSUA_LOCK();

    /* Find which account for the incoming request. */
    acc_id = pjsua_acc_find_for_incoming(rdata);
    acc = &pjsua_var.acc[acc_id];

    PJ_LOG(4,(THIS_FILE, "Creating server subscription, using account %d",
	      acc_id));
    
    /* Create suitable Contact header */
    if (acc->contact.slen) {
	contact = acc->contact;
    } else {
	status = pjsua_acc_create_uas_contact(rdata->tp_info.pool, &contact,
					      acc_id, rdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Unable to generate Contact header", 
			 status);
	    PJSUA_UNLOCK();
	    pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
					  NULL, NULL);
	    return PJ_TRUE;
	}
    }

    /* Create UAS dialog: */
    status = pjsip_dlg_create_uas(pjsip_ua_instance(), rdata, 
				  &contact, &dlg);
    if (status != PJ_SUCCESS) {
	pjsua_perror(THIS_FILE, 
		     "Unable to create UAS dialog for subscription", 
		     status);
	PJSUA_UNLOCK();
	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 400, NULL,
				      NULL, NULL);
	return PJ_TRUE;
    }

    /* 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));
    pres_cb.on_evsub_state = &pres_evsub_on_srv_state;

    /* Create server presence subscription: */
    status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
    if (status != PJ_SUCCESS) {
	int code = PJSIP_ERRNO_TO_SIP_STATUS(status);
	pjsip_tx_data *tdata;

	pjsua_perror(THIS_FILE, "Unable to create server subscription", 
		     status);

	if (code==599 || code > 699 || code < 300) {
	    code = 400;
	}

	status = pjsip_dlg_create_response(dlg, rdata, code, NULL, &tdata);
	if (status == PJ_SUCCESS) {
	    status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
					     tdata);
	}

	PJSUA_UNLOCK();
	return PJ_TRUE;
    }

    /* If account is locked to specific transport, then lock dialog
     * to this transport too.
     */
    if (acc->cfg.transport_id != PJSUA_INVALID_ID) {
	pjsip_tpselector tp_sel;

	pjsua_init_tpselector(acc->cfg.transport_id, &tp_sel);
	pjsip_dlg_set_transport(dlg, &tp_sel);
    }

    /* Attach our data to the subscription: */
    uapres = PJ_POOL_ALLOC_T(dlg->pool, pjsua_srv_pres);
    uapres->sub = sub;
    uapres->remote = (char*) pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
    uapres->acc_id = acc_id;
    uapres->dlg = dlg;
    status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
			     uapres->remote, PJSIP_MAX_URL_SIZE);
    if (status < 1)
	pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
    else
	uapres->remote[status] = '\0';

    pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, uapres);

    /* Add server subscription to the list: */
    pj_list_push_back(&pjsua_var.acc[acc_id].pres_srv_list, uapres);


    /* Capture the value of Expires header. */
    expires_hdr = (pjsip_expires_hdr*)
    		  pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES,
				     NULL);
    if (expires_hdr)
	uapres->expires = expires_hdr->ivalue;
    else
	uapres->expires = -1;

    st_code = (pjsip_status_code)200;
    reason = pj_str("OK");
    pjsua_msg_data_init(&msg_data);

    /* Notify application callback, if any */
    if (pjsua_var.ua_cfg.cb.on_incoming_subscribe) {
	pjsua_buddy_id buddy_id;

	buddy_id = pjsua_find_buddy(rdata->msg_info.from->uri);

	(*pjsua_var.ua_cfg.cb.on_incoming_subscribe)(acc_id, uapres, buddy_id,
						     &dlg->remote.info_str, 
						     rdata, &st_code, &reason,
						     &msg_data);
    }

    /* Handle rejection case */
    if (st_code >= 300) {
	pjsip_tx_data *tdata;

	/* Create response */
	status = pjsip_dlg_create_response(dlg, rdata, st_code, 
					   &reason, &tdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error creating response",  status);
	    pj_list_erase(uapres);
	    pjsip_pres_terminate(sub, PJ_FALSE);
	    PJSUA_UNLOCK();
	    return PJ_FALSE;
	}

	/* Add header list, if any */
	pjsua_process_msg_data(tdata, &msg_data);

	/* Send the response */
	status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata),
					 tdata);
	if (status != PJ_SUCCESS) {
	    pjsua_perror(THIS_FILE, "Error sending response",  status);
	    /* This is not fatal */
	}

	/* Terminate presence subscription */
	pj_list_erase(uapres);
	pjsip_pres_terminate(sub, PJ_FALSE);
	PJSUA_UNLOCK();
	return PJ_TRUE;
    }

    /* Create and send 2xx response to the SUBSCRIBE request: */
    status = pjsip_pres_accept(sub, rdata, st_code, &msg_data.hdr_list);
    if (status != PJ_SUCCESS) {
	pjsua_perror(THIS_FILE, "Unable to accept presence subscription", 
		     status);
	pj_list_erase(uapres);
	pjsip_pres_terminate(sub, PJ_FALSE);
	PJSUA_UNLOCK();
	return PJ_FALSE;
    }

    /* If code is 200, send NOTIFY now */
    if (st_code == 200) {
	pjsua_pres_notify(acc_id, uapres, PJSIP_EVSUB_STATE_ACTIVE, 
			  NULL, NULL, PJ_TRUE, &msg_data);
    }

    /* Done: */

    PJSUA_UNLOCK();

    return PJ_TRUE;
}
Пример #27
0
/*
 * Dump presence subscriptions to log file.
 */
PJ_DEF(void) pjsua_pres_dump(pj_bool_t verbose)
{
    unsigned acc_id;
    unsigned i;

    
    PJSUA_LOCK();

    /*
     * When no detail is required, just dump number of server and client
     * subscriptions.
     */
    if (verbose == PJ_FALSE) {
	
	int count = 0;

	for (acc_id=0; acc_id<PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {

	    if (!pjsua_var.acc[acc_id].valid)
		continue;

	    if (!pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {
		struct pjsua_srv_pres *uapres;

		uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
		while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
		    ++count;
		    uapres = uapres->next;
		}
	    }
	}

	PJ_LOG(3,(THIS_FILE, "Number of server/UAS subscriptions: %d", 
		  count));

	count = 0;

	for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {
	    if (pjsua_var.buddy[i].uri.slen == 0)
		continue;
	    if (pjsua_var.buddy[i].sub) {
		++count;
	    }
	}

	PJ_LOG(3,(THIS_FILE, "Number of client/UAC subscriptions: %d", 
		  count));
	PJSUA_UNLOCK();
	return;
    }
    

    /*
     * Dumping all server (UAS) subscriptions
     */
    PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));

    for (acc_id=0; acc_id<(int)PJ_ARRAY_SIZE(pjsua_var.acc); ++acc_id) {

	if (!pjsua_var.acc[acc_id].valid)
	    continue;

	PJ_LOG(3,(THIS_FILE, "  %.*s",
		  (int)pjsua_var.acc[acc_id].cfg.id.slen,
		  pjsua_var.acc[acc_id].cfg.id.ptr));

	if (pj_list_empty(&pjsua_var.acc[acc_id].pres_srv_list)) {

	    PJ_LOG(3,(THIS_FILE, "  - none - "));

	} else {
	    struct pjsua_srv_pres *uapres;

	    uapres = pjsua_var.acc[acc_id].pres_srv_list.next;
	    while (uapres != &pjsua_var.acc[acc_id].pres_srv_list) {
	    
		PJ_LOG(3,(THIS_FILE, "    %10s %s",
			  pjsip_evsub_get_state_name(uapres->sub),
			  uapres->remote));

		uapres = uapres->next;
	    }
	}
    }

    /*
     * Dumping all client (UAC) subscriptions
     */
    PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));

    if (pjsua_var.buddy_cnt == 0) {

	PJ_LOG(3,(THIS_FILE, "  - no buddy list - "));

    } else {
	for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) {

	    if (pjsua_var.buddy[i].uri.slen == 0)
		continue;

	    if (pjsua_var.buddy[i].sub) {
		PJ_LOG(3,(THIS_FILE, "  %10s %.*s",
			  pjsip_evsub_get_state_name(pjsua_var.buddy[i].sub),
			  (int)pjsua_var.buddy[i].uri.slen,
			  pjsua_var.buddy[i].uri.ptr));
	    } else {
		PJ_LOG(3,(THIS_FILE, "  %10s %.*s",
			  "(null)",
			  (int)pjsua_var.buddy[i].uri.slen,
			  pjsua_var.buddy[i].uri.ptr));
	    }
	}
    }

    PJSUA_UNLOCK();
}