Beispiel #1
0
int tmedia_codec_plugin_register_2(const tmedia_codec_plugin_def_t* plugin, int prio)
{
	tsk_size_t count = 0;
	tsk_bool_t already_registered = tsk_false;
	const tmedia_codec_plugin_def_t* tmp;
	if(!plugin || tsk_strnullORempty(plugin->name) || tsk_strnullORempty(plugin->format) || (prio + 1) >= TMED_CODEC_MAX_PLUGINS){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	
	// count codecs and found if already registered
	while(__tmedia_codec_plugins[count]){ 
		if(__tmedia_codec_plugins[count] == plugin){
			already_registered = tsk_true; 
		}
		++count;
	}
	
	if(count >= TMED_CODEC_MAX_PLUGINS){
		TSK_DEBUG_ERROR("No room");
		return -1;
	}

	// unregister and compact
	if(already_registered){
		tmedia_codec_plugin_unregister(plugin);
		--count;
	}	
	
	 tmp = __tmedia_codec_plugins[prio];
	__tmedia_codec_plugins[prio] = plugin;
	__tmedia_codec_plugins[count] = tmp;// put old codec add prio to the end of the list
	
	return 0;
}
Beispiel #2
0
const char* tnet_transport_dtls_get_local_fingerprint(const tnet_transport_handle_t *handle, tnet_dtls_hash_type_t hash)
{
	const tnet_transport_t *transport = handle;

	if(!transport){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}

	if(!transport->dtls.enabled){
		TSK_DEBUG_ERROR("DTLS not enabled on this transport");
		return tsk_null;
	}
	if(hash > sizeof(transport->dtls.fingerprints)/sizeof(transport->dtls.fingerprints[0])){
		TSK_DEBUG_ERROR("%d not valid for fingerprint hash", hash);
		return tsk_null;
	}
	if(tsk_strnullORempty(transport->tls.pbk)){
		TSK_DEBUG_ERROR("No certificate for which to get fingerprint");
		return tsk_null;
	}

	if(tnet_dtls_get_fingerprint(transport->tls.pbk, &((tnet_transport_t *)transport)->dtls.fingerprints[hash], hash) == 0){
		return (const char*)transport->dtls.fingerprints[hash];
	}
	return tsk_null;
}
Beispiel #3
0
static const tsdp_header_M_t* tmedia_session_ghost_get_lo(tmedia_session_t* self)
{
	tmedia_session_ghost_t* ghost;

	ghost = (tmedia_session_ghost_t*)self;

	if(self->M.lo){
		return self->M.lo;
	}
	else if(!(self->M.lo = tsdp_header_M_create(ghost->media, 0, ghost->proto ? ghost->proto: "RTP/AVP"))){
		TSK_DEBUG_ERROR("Failed to create lo");
		return tsk_null;
	}

	// add format
	if(!tsk_strnullORempty(ghost->first_format)){
		tsk_string_t* fmt = tsk_string_create(ghost->first_format);
		if(!self->M.lo->FMTs){
			self->M.lo->FMTs = tsk_list_create();
		}
		tsk_list_push_back_data(self->M.lo->FMTs, (void**)&fmt);
		TSK_OBJECT_SAFE_FREE(fmt);
	}

	return self->M.lo;
}
Beispiel #4
0
/**@ingroup tmedia_codec_group
* Initialize a Codec 
* @param self The codec to initialize. Could be any type of codec (e.g. @ref tmedia_codec_audio_t or @ref tmedia_codec_video_t).
* @param type
* @param name the name of the codec. e.g. "G.711u" or "G.711a" etc used in the sdp.
* @param desc full description.
* @param format the format. e.g. "0" for G.711.u or "8" for G.711a or "*" for MSRP.
* @retval Zero if succeed and non-zero error code otherwise.
*/
int tmedia_codec_init(tmedia_codec_t* self, tmedia_type_t type, const char* name, const char* desc, const char* format)
{
	if(!self || tsk_strnullORempty(name)){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	self->type = type;
	tsk_strupdate(&self->name, name);
	tsk_strupdate(&self->desc,desc);
	tsk_strupdate(&self->format, format);
	if(!self->bandwidth_max_upload) self->bandwidth_max_upload = (type == tmedia_video ? tmedia_defaults_get_bandwidth_video_upload_max() : INT_MAX); // INT_MAX or <=0 means undefined
	if(!self->bandwidth_max_download) self->bandwidth_max_download = (type == tmedia_video ? tmedia_defaults_get_bandwidth_video_download_max() : INT_MAX); // INT_MAX or <=0 means undefined
	if(!self->in.rate) self->in.rate = self->plugin->rate;
	if(!self->out.rate) self->out.rate = self->plugin->rate;

	if(type & tmedia_audio){
		tmedia_codec_audio_t* audio = TMEDIA_CODEC_AUDIO(self);
		if(!audio->in.ptime) audio->in.ptime = (self->plugin->audio.ptime ? self->plugin->audio.ptime : tmedia_defaults_get_audio_ptime());
		if(!audio->out.ptime) audio->out.ptime = (self->plugin->audio.ptime ? self->plugin->audio.ptime : tmedia_defaults_get_audio_ptime());
		if(!audio->in.channels) audio->in.channels = self->plugin->audio.channels;
		if(!audio->out.channels) audio->out.channels = self->plugin->audio.channels;
		if(!audio->in.timestamp_multiplier) audio->in.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(self->id, self->in.rate);
		if(!audio->out.timestamp_multiplier) audio->out.timestamp_multiplier = tmedia_codec_audio_get_timestamp_multiplier(self->id, self->out.rate);
	}
	// Video flipping: For backward compatibility we have to initialize the default values
	// according to the CFLAGS: 'FLIP_ENCODED_PICT' and 'FLIP_DECODED_PICT'. At any time you
	// can update thse values (e.g. when the device switch from landscape to portrait) using video_session->set();
	else if(type & tmedia_video){
		tmedia_codec_video_t* video = TMEDIA_CODEC_VIDEO(self);
#if FLIP_ENCODED_PICT
		video->out.flip = tsk_true;
#endif
#if FLIP_DECODED_PICT
		video->in.flip = tsk_true;
#endif
		if(!video->in.fps) video->in.fps = self->plugin->video.fps ? self->plugin->video.fps : tmedia_defaults_get_video_fps();
		if(!video->out.fps) video->out.fps = self->plugin->video.fps ? self->plugin->video.fps : tmedia_defaults_get_video_fps();
		if(video->in.chroma == tmedia_chroma_none) video->in.chroma = tmedia_chroma_yuv420p;
		if(video->out.chroma == tmedia_chroma_none) video->out.chroma = tmedia_chroma_yuv420p;

		if(0){ // @deprecated
			if(!video->in.width) video->in.width = video->out.width = self->plugin->video.width;
			if(!video->in.height) video->in.height = video->out.height = self->plugin->video.height;
		}
		else{
			int ret;
			unsigned width, height;
			video->pref_size = tmedia_defaults_get_pref_video_size();
			if((ret = tmedia_video_get_size(video->pref_size, &width, &height)) != 0){
				width = self->plugin->video.width;
				height = self->plugin->video.height;
			}
			if(!video->in.width) video->in.width = video->out.width = width;
			if(!video->in.height) video->in.height = video->out.height = height;
		}
		
	}

	return 0;
}
Beispiel #5
0
int tsip_challenge_get_response(tsip_challenge_t *self, const char* method, const char* uristring, const tsk_buffer_t* entity_body, tsk_md5string_t* response)
{
	if(TSIP_CHALLENGE_IS_DIGEST(self) && self->stack){
		tsk_md5string_t ha1, ha2;
		nonce_count_t nc;

		/* ===
			Calculate HA1 = MD5(A1) = M5(username:realm:secret)
			In case of AKAv1-MD5 and AKAv2-MD5 the secret must be computed as per RFC 3310 + 3GPP TS 206/7/8/9.
			The resulting AKA RES parameter is treated as a "password"/"secret" when calculating the response directive of RFC 2617.
		*/
		if(TSIP_CHALLENGE_IS_AKAv1(self) || TSIP_CHALLENGE_IS_AKAv2(self)){
			char* akaresult = tsk_null;
			tsip_challenge_get_akares(self, TSIP_CHALLENGE_STACK(self)->identity.password, &akaresult);
			if(thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, akaresult, &ha1)){
				// return -1;
			}
			TSK_FREE(akaresult);
		}
		else{
			if(!tsk_strnullORempty(self->ha1_hexstr)){
				// use HA1 provide be the user (e.g. webrtc2sip server will need this to authenticate INVITEs when acting as b2bua)
				memset(ha1, 0, sizeof(tsk_md5string_t));
				memcpy(ha1, self->ha1_hexstr, (TSK_MD5_DIGEST_SIZE << 1));
			}
			else{
				thttp_auth_digest_HA1(TSIP_CHALLENGE_USERNAME(self), self->realm, TSIP_CHALLENGE_STACK(self)->identity.password, &ha1);
			}
		}

		/* ===
			HA2 
		*/
		thttp_auth_digest_HA2(method,
			uristring,
			entity_body,
			self->qop,
			&ha2);

		/* RESPONSE */
		if(self->nc){
			THTTP_NCOUNT_2_STRING(self->nc, nc);
		}
		thttp_auth_digest_response((const tsk_md5string_t *)&ha1, 
			self->nonce,
			nc,
			self->cnonce,
			self->qop,
			(const tsk_md5string_t *)&ha2,
			response);
		
		if(self->qop){
			self->nc++;
		}

		return 0;
	}
	return -1;
}
Beispiel #6
0
//=================================================================================================
//	param object definition
//
static tsk_object_t* tsk_param_ctor(tsk_object_t* self, va_list * app)
{
	tsk_param_t *param = self;
	if(param){
		const char* name = va_arg(*app, const char *);
		const char* value = va_arg(*app, const char *);

		if(!tsk_strnullORempty(name)) {
			param->name = tsk_strdup(name);
			if(!tsk_strnullORempty(value)) {
				param->value = tsk_strdup(value);
			}
		}
	}

	return self;
}
Beispiel #7
0
int tmedia_codec_plugin_register_2(const tmedia_codec_plugin_def_t* plugin, int prio)
{
    tsk_size_t index = 0, max;
    tsk_bool_t already_registered = tsk_false;
    const tmedia_codec_plugin_def_t* tmp;
    if (!plugin || tsk_strnullORempty(plugin->name) || tsk_strnullORempty(plugin->format) || (prio + 1) >= TMED_CODEC_MAX_PLUGINS) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }

    // count codecs and found if already registered
    while (__tmedia_codec_plugins[index]) {
        if (__tmedia_codec_plugins[index] == plugin) {
            already_registered = tsk_true;
        }
        ++index;
    }

    if (index >= TMED_CODEC_MAX_PLUGINS) {
        TSK_DEBUG_ERROR("No room");
        return -1;
    }

	// clamp prio (must be done here before unregistering the plugin)
	max = tmedia_codec_plugin_registered_count(__tmedia_codec_plugins, sizeof(__tmedia_codec_plugins)/sizeof(__tmedia_codec_plugins[0]));
	prio = TSK_CLAMP(0, prio, (int)(max > 0 ? (max - 1) : 0));

    // unregister and compact
    if (already_registered) {
        if (tmedia_codec_plugin_unregister(plugin) == 0) {
            --index;
        }
    }

	

    // put current plugin at prio and old (which was at prio) at the end
    tmp = __tmedia_codec_plugins[prio];
    __tmedia_codec_plugins[index] = tmp;// put old codec add prio to the end of the list
    __tmedia_codec_plugins[prio] = plugin;

    return 0;
}
Beispiel #8
0
/**@ingroup tmedia_codec_group
* Registers a codec plugin.
* @param plugin the definition of the plugin.
* @retval Zero if succeed and non-zero error code otherwise.
* @sa @ref tmedia_codec_create()
*/
int tmedia_codec_plugin_register(const tmedia_codec_plugin_def_t* plugin)
{
	tsk_size_t i;
	if(!plugin || tsk_strnullORempty(plugin->name) || tsk_strnullORempty(plugin->format)){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	/* add or replace the plugin */
	for(i = 0; i<TMED_CODEC_MAX_PLUGINS; i++){
		if(!__tmedia_codec_plugins[i] || (__tmedia_codec_plugins[i] == plugin)){
			__tmedia_codec_plugins[i] = plugin;
			return 0;
		}
	}
	
	TSK_DEBUG_ERROR("There are already %d plugins.", TMED_CODEC_MAX_PLUGINS);
	return -2;
}
Beispiel #9
0
int tnet_dtls_get_fingerprint(const char* certfile, tnet_fingerprint_t* fingerprint, tnet_dtls_hash_type_t hash)
{
#if !HAVE_OPENSSL || !HAVE_OPENSSL_DTLS
	TSK_DEBUG_ERROR("OpenSSL or DTLS not enabled");
	return -200;
#else
	{
		X509* x509;
		BIO* bio;
		int ret = 0;
		const EVP_MD *evp;

		if (tsk_strnullORempty(certfile) || !fingerprint){
			TSK_DEBUG_ERROR("Invalid parameter");
			return -1;
		}

		if (!(evp = _tnet_dtls_get_hash_evp(hash))){
			return -1;
		}

		x509 = tsk_null;
		bio = tsk_null;

		if (!(bio = BIO_new(BIO_s_file()))){
			TSK_DEBUG_ERROR("BIO_new(BIO_s_file()) failed [%s]", ERR_error_string(ERR_get_error(), tsk_null));
			ret = -3;
			goto bail;
		}
		if (BIO_read_filename(bio, certfile) != 1){
			TSK_DEBUG_ERROR("BIO_read_filename(%s) failed [%s]", certfile, ERR_error_string(ERR_get_error(), tsk_null));
			ret = -4;
			goto bail;
		}
		if (!(x509 = PEM_read_bio_X509(bio, tsk_null, 0, tsk_null))){
			TSK_DEBUG_ERROR("PEM_read_bio() failed [%s]", ERR_error_string(ERR_get_error(), tsk_null));
			ret = -5;
			goto bail;
		}
		if ((ret = _tnet_dtls_get_fingerprint(x509, evp, fingerprint))){
			goto bail;
		}

	bail:
		if (bio){
			BIO_free_all(bio);
		}
		return ret;
	}
#endif
}
Beispiel #10
0
/**@ingroup tmedia_codec_group
* Registers a codec plugin.
* @param plugin the definition of the plugin.
* @retval Zero if succeed and non-zero error code otherwise.
* @sa @ref tmedia_codec_create()
*/
int tmedia_codec_plugin_register(const tmedia_codec_plugin_def_t* plugin)
{
    tsk_size_t i;
    if(!plugin || tsk_strnullORempty(plugin->name) || tsk_strnullORempty(plugin->format)) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }

    /* add or replace the plugin */
    for(i = 0; i<TMED_CODEC_MAX_PLUGINS; i++) {
        if(!__tmedia_codec_plugins[i] || (__tmedia_codec_plugins[i] == plugin)) {
            __tmedia_codec_plugins[i] = plugin;
            TSK_DEBUG_INFO("Register codec: %s, %s", plugin->name, plugin->desc);
            return 0;
        }
        if(__tmedia_codec_plugins[i]->codec_id == plugin->codec_id && plugin->codec_id != tmedia_codec_id_none) { // 'tmedia_codec_id_none' is used for fake codecs
            TSK_DEBUG_INFO("Codec Registration: '%s' ignored because '%s' already registered", plugin->desc, __tmedia_codec_plugins[i]->desc);
            return -3;
        }
    }

    TSK_DEBUG_ERROR("There are already %d plugins.", TMED_CODEC_MAX_PLUGINS);
    return -2;
}
Beispiel #11
0
/**@ingroup tmedia_codec_group
* Indicates whether the codec can handle this sdp attribute.
* @param self the codec to match aginst to.
* @param att_name the name of the sdp attribute to match e.g. 'fmtp' or 'imageattr'
* @retval @a tsk_true if the codec can handle this fmtp and @a tsk_false otherwise
*/
tsk_bool_t tmedia_codec_sdp_att_match(const tmedia_codec_t* self, const char* att_name, const char* att_value)
{
    /* checks */
    if(!self || !self->plugin || !self->plugin->sdp_att_match || !att_name) {
        TSK_DEBUG_ERROR("invalid parameter");
        return tsk_false;
    }

    /* if attribute value is null or empty -> always match */
    if(tsk_strnullORempty(att_value)) {
        return tsk_true;
    }
    else {
        return self->plugin->sdp_att_match(self, att_name, att_value);
    }
}
Beispiel #12
0
/**@ingroup tmedia_codec_group
* Initialize a Codec 
* @param self The codec to initialize. Could be any type of codec (e.g. @ref tmedia_codec_audio_t or @ref tmedia_codec_video_t).
* @param type
* @param name the name of the codec. e.g. "G.711u" or "G.711a" etc used in the sdp.
* @param desc full description.
* @param format the format. e.g. "0" for G.711.u or "8" for G.711a or "*" for MSRP.
* @retval Zero if succeed and non-zero error code otherwise.
*/
int tmedia_codec_init(tmedia_codec_t* self, tmedia_type_t type, const char* name, const char* desc, const char* format)
{
	if(!self || tsk_strnullORempty(name)){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}
	self->type = type;
	tsk_strupdate(&self->name, name);
	tsk_strupdate(&self->desc,desc);
	tsk_strupdate(&self->format, format);
	
	// Video flipping: For backward compatibility we have to initialize the default values
	// according to the CFLAGS: 'FLIP_ENCODED_PICT' and 'FLIP_DECODED_PICT'. At any time you
	// can update thse values (e.g. when the device switch from landscape to portrait) using video_session->set();
	if(type & tmedia_video){
		tmedia_codec_video_t* video = TMEDIA_CODEC_VIDEO(self);
#if FLIP_ENCODED_PICT
		video->out.flip = tsk_true;
#endif
#if FLIP_DECODED_PICT
		video->in.flip = tsk_true;
#endif
		if(!video->in.fps) video->in.fps = video->out.fps = self->plugin->video.fps;
		if(video->in.chroma == tmedia_chroma_none) video->in.chroma = tmedia_chroma_yuv420p;
		if(video->out.chroma == tmedia_chroma_none) video->out.chroma = tmedia_chroma_yuv420p;

		if(0){ // @deprecated
			if(!video->in.width) video->in.width = video->out.width = self->plugin->video.width;
			if(!video->in.height) video->in.height = video->out.height = self->plugin->video.height;
		}
		else{
			int ret;
			unsigned width, height;
			video->pref_size = tmedia_defaults_get_pref_video_size();
			if((ret = tmedia_video_get_size(video->pref_size, &width, &height)) != 0){
				width = self->plugin->video.width;
				height = self->plugin->video.height;
			}
			if(!video->in.width) video->in.width = video->out.width = width;
			if(!video->in.height) video->in.height = video->out.height = height;
		}
		
	}

	return 0;
}
Beispiel #13
0
thttp_challenge_t* thttp_challenge_create(tsk_bool_t isproxy, const char* scheme, const char* realm, const char* nonce, const char* opaque, const char* algorithm, const char* qop)
{
    thttp_challenge_t* challenge = tsk_object_new(thttp_challenge_def_t);
    if (challenge) {

        challenge->isproxy = isproxy;
        challenge->scheme = tsk_strdup(scheme);
        challenge->realm = tsk_strdup(realm);
        challenge->nonce = tsk_strdup(nonce);
        challenge->opaque = tsk_strdup(opaque);
        challenge->algorithm = tsk_strdup(algorithm);

        if (!tsk_strnullORempty(qop)) {
            challenge->qop = tsk_strcontains(qop, tsk_strlen(qop), "auth-int") ? "auth-int" :
                             (tsk_strcontains(qop, tsk_strlen(qop), "auth") ? "auth" : tsk_null);
        }

        if (challenge->qop) {
            _thttp_challenge_reset_cnonce(challenge);
        }
    }
    return challenge;
}
Beispiel #14
0
/**
 * Starts the client transaction.
 *
 * @param [in,out]	self	The client transaction to start. 
 * @param [in,out]	request	The SIP/IMS request to send. 
 *
 * @return	Zero if succeed and non-zero error code otherwise. 
**/
int tsip_transac_nict_start(tsip_transac_nict_t *self, const tsip_request_t* request)
{
	int ret = -1;
	if(self && request && !TSIP_TRANSAC(self)->running){
		/* Add branch to the new client transaction
		* - CANCEL will have the same Via and Contact headers as the request it cancel
		* - Transac will use request branch if exit (e.g. when request received over websocket)
		*/
		if((request->firstVia && !tsk_strnullORempty(request->firstVia->branch))){
			tsk_strupdate(&TSIP_TRANSAC(self)->branch, (request->firstVia ? request->firstVia->branch : "doubango"));
		}
		else if((TSIP_TRANSAC(self)->branch = tsk_strdup(TSIP_TRANSAC_MAGIC_COOKIE))){
			tsk_istr_t branch;
			tsk_strrandom(&branch);
			tsk_strcat_2(&(TSIP_TRANSAC(self)->branch), "-%s", branch);
		}

		TSIP_TRANSAC(self)->running = tsk_true;
		self->request = tsk_object_ref((void*)request);

		ret = tsip_transac_fsm_act(TSIP_TRANSAC(self), _fsm_action_send, tsk_null);
	}
	return ret;
}
Beispiel #15
0
// @param str e.g. "1 1 udp 1 192.168.196.1 57806 typ host name video_rtcp network_name {0C0137CC-DB78-46B6-9B6C-7E097FFA79FE} username StFEVThMK2DHThkv password qkhKUDr4WqKRwZTo generation 0"
tnet_ice_candidate_t* tnet_ice_candidate_parse(const char* str)
{
	char *v, *copy;
	int32_t k;
	tnet_ice_candidate_t* candidate;

	if(tsk_strnullORempty(str)){
		TSK_DEBUG_ERROR("Invalid parameter");
		return tsk_null;
	}	

	if(!(candidate = tsk_object_new(&tnet_ice_candidate_def_s))){
		TSK_DEBUG_ERROR("Failed to create candidate");
		return tsk_null;
	}

	k = 0;
	copy = tsk_strdup(str);
	v = strtok(copy, " ");

	while(v){
		switch(k){
			case 0:
				{
					memcpy(candidate->foundation, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->foundation)));
					break;
				}
			case 1:
				{
					candidate->comp_id = atoi(v);
					break;
				}
			case 2:
				{
					candidate->transport_str = tsk_strdup(v);
					break;
				}
			case 3:
				{
					candidate->priority = atoi(v);
					break;
				}
			case 4:
				{
					memcpy(candidate->connection_addr, v, TSK_MIN(tsk_strlen(v), sizeof(candidate->connection_addr)));
					break;
				}
			case 5:
				{
					tnet_family_t family;
					candidate->port = atoi(v);
					family = tnet_get_family(candidate->connection_addr, candidate->port);
					candidate->transport_e = _tnet_ice_candidate_get_transport_type((family == AF_INET6), candidate->transport_str);
					break;
				}
			case 6:
				{
					v = strtok(tsk_null, " ");
					tsk_strupdate(&candidate->cand_type_str, v);
					candidate->type_e = _tnet_ice_candtype_get_transport_type(v);
					break;
				}
			default:
				{
					const char* name = v;
					const char* value = (v = strtok(tsk_null, " "));
					tsk_param_t* param = tsk_param_create(name, value);
					if(param){
						tsk_list_push_back_data(candidate->extension_att_list, (void**)&param);
					}
					break;
				}
		}

		++k;
		v = strtok(tsk_null, " ");
	}

	if(k < 6){
		TSK_DEBUG_ERROR("Failed to parse: %s", str);
		TSK_OBJECT_SAFE_FREE(candidate);
	}
	TSK_FREE(copy);

	return candidate;
}
Beispiel #16
0
thttp_header_t *thttp_challenge_create_header_authorization_2(thttp_challenge_t *self, const char* username, const char* password, const char* method, const char *uristring, const tsk_buffer_t* entity_body)
{
    char* response = tsk_null;
    tsk_size_t response_size = 0;
    nonce_count_t nc;
    thttp_header_t *header = tsk_null;

    if (!self || tsk_strnullORempty(uristring)) {
        TSK_DEBUG_ERROR("Invalid parameter");
        goto bail;
    }

    /* We compute the nc here because @ref thttp_challenge_get_response function will increment it's value. */
    if (self->nc) {
        THTTP_NCOUNT_2_STRING(self->nc, nc);
    }

    /* Computes the response (Basic and Digest)*/
    if (THTTP_CHALLENGE_IS_DIGEST(self)) {
        if (thttp_challenge_get_digest_response(self, username, password, method, uristring, entity_body, &response)) {
            goto bail;
        }
        response_size = (TSK_MD5_DIGEST_SIZE * 2);
    }
    else if (THTTP_CHALLENGE_IS_BASIC(self)) {
        response_size = thttp_auth_basic_response(username, password, &response);
    }
    else {
        TSK_DEBUG_ERROR("%s not supported as scheme.", self->scheme);
        goto bail;
    }


#define THTTP_AUTH_COPY_VALUES(hdr)															\
hdr->username = tsk_strdup(username);												\
hdr->scheme = tsk_strdup(self->scheme);												\
hdr->realm = tsk_strdup(self->realm);												\
hdr->nonce = tsk_strdup(self->nonce);												\
hdr->qop = tsk_strdup(self->qop);													\
hdr->opaque = tsk_strdup(self->opaque);												\
hdr->algorithm = self->algorithm ? tsk_strdup(self->algorithm) : tsk_strdup("MD5");	\
hdr->cnonce = self->nc? tsk_strdup(self->cnonce) : 0;								\
hdr->uri = tsk_strdup(uristring);													\
hdr->nc = self->nc? tsk_strdup(nc) : 0;												\
hdr->response = tsk_strndup(response, response_size);								\
 
    if (self->isproxy) {
        thttp_header_Proxy_Authorization_t *proxy_auth = thttp_header_authorization_create(); // Very bad way to create Proxy_auth header.
        THTTP_HEADER(proxy_auth)->type = thttp_htype_Proxy_Authorization;

        THTTP_AUTH_COPY_VALUES(proxy_auth);
        header = THTTP_HEADER(proxy_auth);
    }
    else {
        thttp_header_Authorization_t *auth = thttp_header_authorization_create();
        THTTP_AUTH_COPY_VALUES(auth);
        header = THTTP_HEADER(auth);
    }

bail:
    TSK_FREE(response);

    return header;

#undef THTTP_AUTH_COPY_VALUES
}
Beispiel #17
0
int tnet_transport_tls_set_certs(tnet_transport_handle_t *handle, const char* ca, const char* pbk, const char* pvk, tsk_bool_t verify)
{
	tnet_transport_t *transport = handle;
	static const char* ssl_password = tsk_null;
	
	if(!transport){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	tsk_strupdate(&transport->tls.ca, ca);
	tsk_strupdate(&transport->tls.pvk, pvk);
	tsk_strupdate(&transport->tls.pbk, pbk);
	transport->tls.verify = verify;

#if HAVE_OPENSSL
	{
		int32_t i, ret;
		SSL_CTX* contexts[3] = { tsk_null };

		/* init DTLS/TLS contexts */
		if((ret = _tnet_transport_ssl_init(transport))){
			return ret;
		}

		if(transport->tls.enabled){
			contexts[0] = transport->tls.ctx_client;
			contexts[1] = transport->tls.ctx_server;
		}
		if(transport->dtls.enabled){
			contexts[2] = transport->dtls.ctx;
			/* Reset fingerprints */
			memset(transport->dtls.fingerprints, 0, sizeof(transport->dtls.fingerprints));
		}

		for(i = 0; i < sizeof(contexts)/sizeof(contexts[0]); ++i){
			if(!contexts[i]){
				continue;
			}
			SSL_CTX_set_verify(contexts[i], transport->tls.verify ? (SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT) : SSL_VERIFY_NONE, tsk_null);
			if(!tsk_strnullORempty(transport->tls.pbk) || !tsk_strnullORempty(transport->tls.pvk) || !tsk_strnullORempty(transport->tls.ca)){
				/* Sets Public key (cert) */
				if(!tsk_strnullORempty(transport->tls.pbk) && (ret = SSL_CTX_use_certificate_file(contexts[i], transport->tls.pbk, SSL_FILETYPE_PEM)) != 1) {
					TSK_DEBUG_ERROR("SSL_CTX_use_certificate_file failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null));
					return -3;
				}
				/*Sets the password of the private key*/
				if(!tsk_strnullORempty(ssl_password)){
					SSL_CTX_set_default_passwd_cb_userdata(contexts[i], (void*)ssl_password);
				}

				/* Sets Private key (cert) */
				if (!tsk_strnullORempty(transport->tls.pvk) && (ret = SSL_CTX_use_PrivateKey_file(contexts[i], transport->tls.pvk, SSL_FILETYPE_PEM)) != 1) {
					TSK_DEBUG_ERROR("SSL_CTX_use_PrivateKey_file failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null));
					return -4;
				}
				/* Checks private key */
				if(!tsk_strnullORempty(transport->tls.pvk) && SSL_CTX_check_private_key(contexts[i]) == 0) {
					TSK_DEBUG_ERROR("SSL_CTX_check_private_key failed [%d,%s]", ret, ERR_error_string(ERR_get_error(), tsk_null));
					return -5;
				}
				/* Sets trusted CAs and CA file */
				if(!tsk_strnullORempty(transport->tls.ca) && (ret = SSL_CTX_load_verify_locations(contexts[i], transport->tls.ca, /*tlsdir_cas*/tsk_null)) != 1) {
				   TSK_DEBUG_ERROR("SSL_CTX_load_verify_locations failed [%d, %s]", ret, ERR_error_string(ERR_get_error(), tsk_null));
				   return -5;
				}
			}
		}
	}
#endif /* HAVE_OPENSSL */

	return 0;
}
Beispiel #18
0
int tdav_session_msrp_set(tmedia_session_t* self, const tmedia_param_t* param)
{
	int ret = 0;
	tdav_session_msrp_t* msrp;

	if(!self){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	TSK_DEBUG_INFO("tdav_session_msrp_set");

	msrp = (tdav_session_msrp_t*)self;

	if(param->value_type == tmedia_pvt_pchar){
		if(tsk_striequals(param->key, "remote-ip")){
			// only if no ip associated to the "m=" line
			if(param->value && !msrp->remote_ip){
				msrp->remote_ip = tsk_strdup(param->value);
			}
		}
		else if(tsk_striequals(param->key, "local-ip")){
			tsk_strupdate(&msrp->local_ip, param->value);
		}
		else if(tsk_striequals(param->key, "local-ipver")){
			msrp->useIPv6 = tsk_striequals(param->value, "ipv6");
		}
		else if(tsk_striequals(param->key, "accept-types")){
			tsk_strupdate(&msrp->accept_types, param->value);
		}
		else if(tsk_striequals(param->key, "accept-wrapped-types")){
			tsk_strupdate(&msrp->accept_w_types, param->value);
		}

		/* Configuration */
		else if(tsk_striequals(param->key, "Failure-Report")){
			msrp->config->Failure_Report = tsk_striequals(param->value, "yes");
		}
		else if(tsk_striequals(param->key, "Success-Report")){
			msrp->config->Success_Report = tsk_striequals(param->value, "yes");
		}

		/* File Transfer */
		else if(tsk_striequals(param->key, "file-path") && !tsk_strnullORempty((const char*)param->value)){
			tsk_strupdate(&msrp->file.path, param->value);
		}
		else if(tsk_striequals(param->key, "file-selector")){
			tsk_strupdate(&msrp->file.selector, param->value);
		}
		else if(tsk_striequals(param->key, "file-disposition")){
			tsk_strupdate(&msrp->file.disposition, param->value);
		}
		else if(tsk_striequals(param->key, "file-date")){
			tsk_strupdate(&msrp->file.date, param->value);
		}
		else if(tsk_striequals(param->key, "file-icon")){
			tsk_strupdate(&msrp->file.icon, param->value);
		}
		else if(tsk_striequals(param->key, "file-transfer-id")){
			tsk_strupdate(&msrp->file.transfer_id, param->value);
		}
	}
	else if(param->value_type == tmedia_pvt_pobject){
		if(tsk_striequals(param->key, "natt-ctx")){
			TSK_OBJECT_SAFE_FREE(msrp->natt_ctx);
			msrp->natt_ctx = tsk_object_ref(param->value);
		}
	}
	else if(param->value_type == tmedia_pvt_int64 || param->value_type == tmedia_pvt_int32){
		if(tsk_striequals(param->key, "chunck-duration")){
			msrp->chunck_duration = TSK_TO_UINT32((uint8_t*)param->value);
			if(msrp->sender){
				msrp->sender->chunck_duration = msrp->chunck_duration;
			}
		}
	}

	return ret;
}
Beispiel #19
0
/* === entry point === */
int main(int argc, char** argv)
{
	char cmdbuf[4096];
	tsk_buffer_t* buffer = tsk_null;
	cmd_t* cmd = tsk_null;
	tsk_bool_t comment = tsk_false;
	int ret;
	int i, index;
	const char* start = tsk_null, *end = tsk_null;

	int a = 32 | 1 | 2;

	/* Copyright */
	printf("Doubango Project (tinyDEMO)\nCopyright (C) 2009 - 2013 Mamadou Diop \n\n");

	/* Initialize Network Layer ==> Mandatory */
	tnet_startup();
	/* Initialize Doubango Audio/Video Framework ==> will register all plugins(codecs and sessions) 
	* Not mandatory if you have your own plugins*/
	tdav_init();

	/* Print Usage */
	//cmd_print_help();

	/* create user's ctx */
	if(!(ctx = ctx_create()) || !ctx->stack){
		TSK_DEBUG_ERROR("Failed to create user's ctx.");
		goto bail;
	}

	/* create new buffer */
	if(!(buffer = tsk_buffer_create_null())){
		TSK_DEBUG_ERROR("Failed to create new buffer.");
		goto bail;
	}

	/* initial args */
	for(i=1 /* index zero contains the exe path */, index=0; i<argc && argv[i]; i++){
		if(index){
			tsk_buffer_append(buffer, " ", 1);
		}
		tsk_buffer_append(buffer, argv[i], tsk_strlen(argv[i]));
	}
	
	/* If initial args ==> parse it now */
	if(buffer->size){
		TSK_DEBUG_INFO("Initial command-line: %s", buffer->data);
		goto init_buffer;
	}

	/* always use fgets() instead of gets. gets() is considered to be unsafe.(Android and Mac OS X will warn) */
	while(fgets(cmdbuf, sizeof(cmdbuf), stdin)){
		TSK_DEBUG_INFO("Command-Line: %s", cmdbuf);
		tsk_buffer_cleanup(buffer); /* cannot read from console while executing scenario */
		tsk_buffer_append(buffer, cmdbuf, tsk_strlen(cmdbuf));
init_buffer:
		start = buffer->data;
		//start = trim(start);
		end = start + buffer->size;
		if(start >= end){
			TSK_DEBUG_INFO("Empty buffer");
			continue;
		}
parse_buffer:
		TSK_OBJECT_SAFE_FREE(cmd); /* Free old value */
		cmd = cmd_parse(start, (end-start), &comment, ctx->params);
		if(cmd){
			if(comment || cmd->type == cmd_none){
				goto nex_line;
			}
		}
		else{
			continue;
		}

		/* Load from scenario file? */
		if(cmd->type == cmd_scenario){
			FILE* file;
			const opt_t* opt;
			tsk_size_t read = 0;
			tsk_bool_t rm_lf = tsk_false;
			if((opt = opt_get_by_type(cmd->opts, opt_path)) && !tsk_strnullORempty(opt->value)){ /* --path option */
				if((file = fopen(opt->value, "r"))){
					memset(cmdbuf, '\0', sizeof(cmdbuf)), cmdbuf[0] = '\n';
					read = fread(cmdbuf+1, sizeof(uint8_t), sizeof(cmdbuf)-1, file);
					fclose(file), file = tsk_null;

					if(read == 0){
						TSK_DEBUG_ERROR("[%s] is empty.", opt->value);
						goto nex_line;
					}
					else if(read == sizeof(cmdbuf)-1){
						TSK_DEBUG_ERROR("Buffer too short.");
						
						goto nex_line;
					}
					read++; /* \n */
					/* repplace all '\' with spaces (easier than handling that in the ragel file) */
					for(i=0; ((tsk_size_t)i)<read; i++){
						if(cmdbuf[i] == '\\'){
							cmdbuf[i] = ' ';
							rm_lf = tsk_true;
						}
						else if(rm_lf && cmdbuf[i] == '\n'){
							cmdbuf[i] = ' ';
							rm_lf = tsk_false;
						}
					}
					cmdbuf[read] = '\n';
					
					/* insert embedded scenario */
					if((index = tsk_strindexOf(start, (end-start), "\n")) == -1){ /* ++sn line */
						index = buffer->size;
					}
					else{
						index += (start - ((const char*)buffer->data));
					}
				
					if(tsk_buffer_insert(buffer, index, cmdbuf, read)){
						continue;
					}
					else{
						start = ((const char*)buffer->data) + index; // because insert use realloc()
						end = (((const char*)buffer->data) + buffer->size);
						goto nex_line;
					}
				}
				else{
					TSK_DEBUG_ERROR("Failed to open scenario-file [%s].", opt->value);
					goto nex_line;
				}
				continue;
			}
			else{
				TSK_DEBUG_ERROR("++scenario command must have --path option.");
				continue;
			}
		}
		
		/* execute current command */
		switch(cmd->type){
			case cmd_exit:
					TSK_DEBUG_INFO("Exit/Quit");
					goto bail;
			default:
				ret = execute(cmd);
				break;
		}

		/* next line */
nex_line:
		if((index = tsk_strindexOf(start, (end - start), "\n")) !=-1){
			start += index;
			while((start < end) && isspace(*start)){
				start ++;
			}
			if((start + 2/*++*/) < end){
				goto parse_buffer; /* next line */
			}
			else{
				continue; /* wait for new commands */
			}
		}
	} /* while(buffer) */


bail:
	
	/* Free current command */
	TSK_OBJECT_SAFE_FREE(cmd);
	/* Free buffer */
	TSK_OBJECT_SAFE_FREE(buffer);
	/* Destroy the user's ctx */
	TSK_OBJECT_SAFE_FREE(ctx);
	/* Deinitialize Doubango Audio/Video Framework ==> will unregister all plugins(codecs and sessions) 
	* Not mandatory */
	tdav_init();
	/* Uninitilize Network Layer */
	tnet_cleanup();

#if ANDROID
	exit(0);
#endif
	return 0;
}
Beispiel #20
0
int execute(const cmd_t* cmd)
{
	int ret = 0;
	tsip_ssession_id_t sid;
	tsk_istr_t istr;

	if(!cmd){
		TSK_DEBUG_ERROR("Invalid parameter");
		return -1;
	}

	tsk_safeobj_lock(ctx);

	switch(cmd->type){
		case cmd_audio:
		case cmd_audiovideo:
			{
				TSK_DEBUG_INFO("command=audio/video");
				if((sid = invite_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_config_session:
			{
				TSK_DEBUG_INFO("command=config-session");
				break;
			}
		case cmd_config_stack:
			{
				TSK_DEBUG_INFO("command=config-satck");
				ret = stack_config(cmd->opts);
				break;
			}
		case cmd_dtmf:
			{
				const opt_t* opt;
				TSK_DEBUG_INFO("command=dtmf");
				if(!(opt = opt_get_by_type(cmd->opts, opt_sid)) || tsk_strnullORempty(opt->value)){ /* --sid option */
					TSK_DEBUG_ERROR("++dtmf command need --sid option");
					break;
				}
				if(!(opt = opt_get_by_type(cmd->opts, opt_event)) || tsk_strnullORempty(opt->value)){ /* --event option */
					TSK_DEBUG_ERROR("++dtmf command need --event option");
					break;
				}
				invite_handle_cmd(cmd->type, cmd->opts);
				break;
			}
		case cmd_dump:
			{
				TSK_DEBUG_INFO("command=dump");
				ret = stack_dump();
				break;
			}
		case cmd_ect:
		{
			const opt_t* opt;
			TSK_DEBUG_INFO("command=ect");
			if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)){
				TSK_DEBUG_ERROR("++ect command need --sid option");
				ret = -1;
				break;
			}
			if((opt = opt_get_by_type(cmd->opts, opt_to)) && !tsk_strnullORempty(opt->value)){
				TSK_DEBUG_ERROR("++ect command need --to option");
				ret = -1;
				break;
			}
			invite_handle_cmd(cmd->type, cmd->opts);
			break;
		}
		case cmd_exit:
			{
				TSK_DEBUG_INFO("command=exit");
				goto bail;
				break;
			}
		case cmd_file:
			{
				const opt_t* opt;
				TSK_DEBUG_INFO("command=file");
				if(!(opt = opt_get_by_type(cmd->opts, opt_path)) || tsk_strnullORempty(opt->value)){
					TSK_DEBUG_ERROR("++file command need --path option");
					break;
				}
				if((sid = invite_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_hangup:
			{
				const opt_t* opt;
				TSK_DEBUG_INFO("command=hangup");
				if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)){ /* --sid option */
					ret = session_hangup(tsk_atoll(opt->value));
				}
				else{
					TSK_DEBUG_ERROR("++hangup command need --sid option");
					ret = -1;
				}
				break;
			}
		case cmd_help:
			{
				TSK_DEBUG_INFO("command=help");
				cmd_print_help();
				break;
			}
		case cmd_hold:
			{
				const opt_t* opt;
				TSK_DEBUG_INFO("command=hold");
				if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)){ /* --sid option */
					invite_handle_cmd(cmd->type, cmd->opts);
				}
				else{
					TSK_DEBUG_ERROR("++hold command need --sid option");
					ret = -1;
				}
				break;
			}
		case cmd_message:
			{
				TSK_DEBUG_INFO("command=message");
				if((sid = message_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}

		case cmd_options:
			{
				TSK_DEBUG_INFO("command=options");
				if((sid = options_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_publish:
			{
				TSK_DEBUG_INFO("command=publish");
				if((sid = publish_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_register:
			{
				TSK_DEBUG_INFO("command=register");
				if((sid = register_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_resume:
			{
				const opt_t* opt;
				TSK_DEBUG_INFO("command=resume");
				if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)){ /* --sid option */
					invite_handle_cmd(cmd->type, cmd->opts);
				}
				else{
					TSK_DEBUG_ERROR("++resume command need --sid option");
					ret = -1;
				}
				break;
			}
		case cmd_run:
			{
				TSK_DEBUG_INFO("command=run");
				ret = stack_run(cmd->opts);
				break;
			}
		case cmd_scenario:
			{
				TSK_DEBUG_INFO("command=scenario");
				break;
			}
		case cmd_sleep:
			{
				const opt_t* opt;
				double seconds;

				tsk_safeobj_unlock(ctx); /* beacuse of callback function */

				if((opt = opt_get_by_type(cmd->opts, opt_sec)) && !tsk_strnullORempty(opt->value)){ /* --sec option */
					seconds = strtod(opt->value, tsk_null); /* strtod() is better than atof() */
					if(seconds<=0){
						printf("\n==== Press ENTER to continue...\n");
						getchar();
					}
					else{
						TSK_DEBUG_INFO("Sleeping %f seconds", seconds);
						tsk_thread_sleep((uint64_t)(seconds * 1000));
					}
				}
				else{
					TSK_DEBUG_WARN("++sleep need --sec option.");
				}
				return 0; /* bail: will unlock again */
			}
		case cmd_sms:
			{
				TSK_DEBUG_INFO("command=sms");
				if((sid = message_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_stop:
			{
				TSK_DEBUG_INFO("command=stop");
				tsip_stack_stop(ctx->stack);
				break;
			}
		case cmd_subscribe:
			{
				TSK_DEBUG_INFO("command=subscribe");
				if((sid = subscribe_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID){
					if(cmd->sidparam){
						tsk_itoa(sid, &istr);
						update_param(cmd->sidparam, istr);
					}
				}
				break;
			}
		case cmd_video:
			{
				TSK_DEBUG_INFO("command=video");
				break;
			}
		default:
			{
				TSK_DEBUG_ERROR("%d not a valid command.", cmd);
				break;
			}
	}

bail:
	tsk_safeobj_unlock(ctx);

	return ret;
}
Beispiel #21
0
/**@ingroup tmedia_codec_group
*/
int tmedia_codec_parse_fmtp(const char* fmtp, unsigned* maxbr, unsigned* fps, unsigned *width, unsigned *height)
{
    char *copy, *pch, *saveptr;
    tsk_bool_t found = tsk_false;

    if(tsk_strnullORempty(fmtp)) {
        TSK_DEBUG_ERROR("Invalid parameter");
        return -1;
    }

    copy = tsk_strdup(fmtp);
    pch = tsk_strtok_r(copy, "; /", &saveptr);

    while(pch) {
        unsigned div = 0;

        if(sscanf(pch, "QCIF=%u", &div) == 1 && div) {
            *fps = 30/div;
            *width = 176;
            *height = 144;
            found = tsk_true;
        }
        else if(sscanf(pch, "CIF=%u", &div) == 1 && div) {
            *fps = 30/div;
            *width = 352;
            *height = 288;
            found = tsk_true;
        }
        else if(sscanf(pch, "SQCIF=%u", &div) == 1 && div) {
            *fps = 30/div;
            *width = 128;
            *height = 96;
            found = tsk_true;
        }
        else if(sscanf(pch, "QVGA=%u", &div) == 1 && div) {
            *fps = 30/div;
            *width = 320;
            *height = 240;
            found = tsk_true;
        }
        // to be continued

        if(found) {
            //found = tsk_false;
            pch = tsk_strtok_r(tsk_null, "; ", &saveptr);
            while(pch) {
                if(sscanf(pch, "MaxBR=%u", maxbr) == 1) {
                    //found = tsk_true;
                    break;
                }
                pch = tsk_strtok_r(tsk_null, "; /", &saveptr);
            }
        }

        if(found) {
            break;
        }

        pch = tsk_strtok_r(tsk_null, "; /", &saveptr);
    }

    TSK_FREE(copy);

    return found ? 0 : -2;
}
Beispiel #22
0
const char* tnet_ice_candidate_tostring(tnet_ice_candidate_t* self)
{
	const char* _transport_str;
	char __str[16]; // always allocated: bad idea :(

	if(!self){
		TSK_DEBUG_ERROR("Invalid argument");
		return tsk_null;
	}
	
	_transport_str = self->transport_str ? self->transport_str : _tnet_ice_candidate_get_transport_str(self->transport_e);
	if(self->is_ice_jingle){
		tsk_size_t i, s = tsk_strlen(_transport_str);
		memset(__str, 0, sizeof(__str));
		for(i = 0; i < s && i < sizeof(__str)/sizeof(__str[0]); ++i){
			__str[i] = tolower(_transport_str[i]);
		}
		_transport_str = &__str[0];
	}

	_tnet_ice_candidate_tostring(
		self->foundation,
		self->comp_id,
		_transport_str,
		self->priority,
		(tsk_strnullORempty(self->connection_addr) && self->socket) ? self->socket->ip : self->connection_addr,
		(self->port <= 0 && self->socket) ? self->socket->port : self->port,
		self->cand_type_str ? self->cand_type_str : _tnet_ice_candidate_get_candtype_str(self->type_e),
		self->extension_att_list,
		&self->tostring);

	/* <rel-addr> and <rel-port>:  convey transport addresses related to the
      candidate, useful for diagnostics and other purposes. <rel-addr>
      and <rel-port> MUST be present for server reflexive, peer
      reflexive, and relayed candidates. */
	switch(self->type_e){
		case tnet_ice_cand_type_srflx:
		case tnet_ice_cand_type_prflx:
		case tnet_ice_cand_type_relay:
			{
				if(self->socket){ // when called from the browser(IE, Safari, Opera or Firefox) webrtc4all
					tsk_strcat_2(&self->tostring, " raddr %s rport %d", self->socket->ip, self->socket->port);
				}
				break;
			}
	}

	// WebRTC (Chrome) specific
	if(self->is_ice_jingle){
		if(!tsk_params_have_param(self->extension_att_list, "name")){
			tsk_strcat_2(&self->tostring, " name %s", self->is_rtp ? (self->is_video ? "video_rtp" : "rtp") : (self->is_video ? "video_rtcp" : "rtcp"));
		}
		if(!tsk_params_have_param(self->extension_att_list, "username")){
			tsk_strcat_2(&self->tostring, " username %s", self->ufrag);
		}
		if(!tsk_params_have_param(self->extension_att_list, "password")){
			tsk_strcat_2(&self->tostring, " password %s", self->pwd);
		}
		if(!tsk_params_have_param(self->extension_att_list, "network_name")){
			tsk_strcat_2(&self->tostring, " network_name %s", "{9EBBE687-CCE6-42D3-87F5-B57BB30DEE23}");
		}
		if(!tsk_params_have_param(self->extension_att_list, "generation")){
			tsk_strcat_2(&self->tostring, " generation %s", "0");
		}
	}
	
	return self->tostring;
}