Linphone-android 登入過程增加自定義訊息頭流程分析
註冊增加訊息頭
在saveNewAccount()中;
新增自定義訊息頭
LinphoneProxyConfig prxCfg = lc.createProxyConfig(identityAddr.asString(), proxyAddr.asStringUriOnly(), route, tempEnabled);
prxCfg.setCustomHeader("key","hello key");
prxCfg.setCustomHeader("key2","hello key2");
setCustomHeader(); 在linphonecore_jni.cc line5195
JNIEXPORT void JNICALL Java_org_linphone_core_LinphoneProxyConfigImpl_setCustomHeader(JNIEnv *env, jobject thiz, jlong prt, jstring jname, jstring jvalue) {
const char *name = GetStringUTFChars(env, jname);
const char *value = GetStringUTFChars(env, jvalue);
linphone_proxy_config_set_custom_header((LinphoneProxyConfig*) prt, name, value );
ReleaseStringUTFChars(env, jname, name);
ReleaseStringUTFChars(env, jvalue, value);
}
linphone_proxy_config_set_custom_header(cfg,name,value);在proxy.c line960
void linphone_proxy_config_set_custom_header(LinphoneProxyConfig *cfg, const char *header_name, const char *header_value){
cfg->sent_headers=sal_custom_header_append(cfg->sent_headers, header_name, header_value);
cfg->register_changed = TRUE;
}
sal_custom_header_append();在sal_impl.c line970
SalCustomHeader *sal_custom_header_append(SalCustomHeader *ch, const char *name, const char *value){
belle_sip_message_t *msg=(belle_sip_message_t*)ch;
belle_sip_header_t *h;
if (msg==NULL){
msg=(belle_sip_message_t*)belle_sip_request_new();
belle_sip_object_ref(msg);
}
h=belle_sip_header_create(name,value);
if (h==NULL){
belle_sip_error("Fail to parse custom header.");
return (SalCustomHeader*)msg;
}
belle_sip_message_add_header(msg,h);
return (SalCustomHeader*)msg;
}
其中包含belle_sip_header_create(name,value) 在belle_sip_headers_impl.c line110
和belle_sip_message_add_header(msg,h)在message.c line 178
belle_sip_header_t* belle_sip_header_create(const char* name, const char* value) {
return belle_header_create(name,value,PROTO_SIP);
}
belle_header_create(name,value,PROTO_SIP) belle_sip_header_impl.c line 88
static belle_sip_header_t* belle_header_create(const char* name,const char* value,int protocol) {
size_t i;
belle_sip_header_t* ret;
size_t elements =sizeof(header_table)/sizeof(struct header_name_func_pair);
if (!name || name[0]=='\0') {
belle_sip_error("Cannot create header without name");
return NULL;
}
for(i=0;i<elements;i++) {
if ((header_table[i].protocol & protocol) && strcasecmp(header_table[i].name,name)==0) {
char* raw = belle_sip_strdup_printf("%s:%s",name,value);
ret=header_table[i].func(raw);
belle_sip_free(raw);
return ret;
}
}
/*not a known header*/
return BELLE_SIP_HEADER(belle_sip_header_extension_create(name,value));
}
其中 header_table[]是一個定義好的陣列,形式如下:
static struct header_name_func_pair header_table[] = {
{PROTO_SIP, "m", (header_parse_func)belle_sip_header_contact_parse}
,{PROTO_SIP, BELLE_SIP_CONTACT, (header_parse_func)belle_sip_header_contact_parse}
,{PROTO_SIP, "f", (header_parse_func)belle_sip_header_from_parse}
......
}
通過for迴圈,檢查傳入的protocol和name是否與table中預定義的一致,如果一致,將name與value組合,作為入參呼叫table中的function,獲取最終的header;如果name不在table定義的範圍內,呼叫belle_sip_header_extension_create()來構建header;
belle_sip_header_extension_create(); belle_sip_headers_impl.c line 1122;
belle_sip_header_extension_t* belle_sip_header_extension_create (const char* name,const char* value) {
belle_sip_header_extension_t* ext = belle_sip_header_extension_new();
belle_sip_header_set_name(BELLE_SIP_HEADER(ext),name);
belle_sip_header_extension_set_value(ext,value);
return ext;
}
至此 自定義頭的belle_sip_header_t* 的結構體構建好了
回到sal_custom_header_append函式中,第二部執行add_header;
belle_sip_message_add_header 在message.c line 178
void belle_sip_message_add_header(belle_sip_message_t *message,belle_sip_header_t* header) {
char* header_string=belle_sip_object_to_string(header);
belle_sip_message("belle_sip_message_add_header [%s]",header_string);
headers_container_t *headers_container=get_or_create_container(message,belle_sip_header_get_name(header));
headers_container->header_list=belle_sip_list_append(headers_container->header_list,belle_sip_object_ref(header));
}
其中的get_or_create_container(message,xxxx) line 163
headers_container_t * get_or_create_container(belle_sip_message_t *message, const char *header_name){
// first check if already exist
headers_container_t* headers_container = belle_sip_headers_container_get(message,header_name);
if (headers_container == NULL) {
headers_container = belle_sip_message_headers_container_new(header_name);
message->header_list=belle_sip_list_append(message->header_list,headers_container);
}
return headers_container;
}
message有一個header_list的屬性,可能以name作為鍵值,來存貯每一對header;
headner_container可能是這個header_list的容器;
這個函式主要從message中獲取name鍵值的container返回;
然後在belle_sip_message_add_header中追加新的header;
回到sal_custom_header_append()內;message中加入了新的header,並返回message;
回到linphone_proxy_config_set_custom_header()內,獲得的message被設定到cfg->sent_headers;
至此自定義訊息頭的新增結束。但是並沒有真正傳送sip訊息給伺服器;
**
註冊賬號
**
在java的使用者註冊操作如下:
lc.addProxyConfig(prxCfg);
lc.addAuthInfo(authInfo);
先看第一個lc.addProxyConfig();
jni的介面如下,在linphonecore_jni.cc line1782
extern "C" jint Java_org_linphone_core_LinphoneCoreImpl_addProxyConfig( JNIEnv* env
,jobject thiz
,jobject jproxyCfg
,jlong lc
,jlong pc) {
LinphoneProxyConfig* proxy = (LinphoneProxyConfig*)pc;
return (jint)linphone_core_add_proxy_config((LinphoneCore*)lc,proxy);
}
真實實現在linphone_core_add_proxy_config(lc,proxy)中 proxy.c line965
int linphone_core_add_proxy_config(LinphoneCore *lc, LinphoneProxyConfig *cfg){
if (!linphone_proxy_config_check(lc,cfg)) {
return -1;
}
if (bctbx_list_find(lc->sip_conf.proxies,cfg)!=NULL){
ms_warning("ProxyConfig already entered, ignored.");
return 0;
}
lc->sip_conf.proxies=bctbx_list_append(lc->sip_conf.proxies,(void *)linphone_proxy_config_ref(cfg));
linphone_proxy_config_apply(cfg,lc);
return 0;
}
此處做的工作不多,只是將傳入的cfg追加到lc->sip_conf.proxies列表中儲存起來;
然後通過linphone_proxy_config_apply(cfg,lc)儲存起來;
linphone_proxy_config_apply(cfg,lc) proxy.c line386
void linphone_proxy_config_apply(LinphoneProxyConfig *cfg,LinphoneCore *lc){
cfg->lc=lc;
linphone_proxy_config_done(cfg);
}
先將新的lc關聯到cfg,然後進行config的儲存
linphone_proxy_config_done(cfg) proxy.c line765
int linphone_proxy_config_done(LinphoneProxyConfig *cfg)
{
LinphoneProxyConfigAddressComparisonResult res;
if (!linphone_proxy_config_check(cfg->lc,cfg))
return -1;
/*check if server address has changed*/
res = linphone_proxy_config_is_server_config_changed(cfg);
if (res != LinphoneProxyConfigAddressEqual) {
/* server config has changed, need to unregister from previous first*/
if (cfg->op) {
if (res == LinphoneProxyConfigAddressDifferent) {
_linphone_proxy_config_unregister(cfg);
}
sal_op_set_user_pointer(cfg->op,NULL); /*we don't want to receive status for this un register*/
sal_op_unref(cfg->op); /*but we keep refresher to handle authentication if needed*/
cfg->op=NULL;
}
if (cfg->long_term_event) {
if (res == LinphoneProxyConfigAddressDifferent) {
_linphone_proxy_config_unpublish(cfg);
}
}
cfg->commit = TRUE;
}
if (cfg->register_changed){
cfg->commit = TRUE;
cfg->register_changed = FALSE;
}
if (cfg->commit){
linphone_proxy_config_pause_register(cfg);
}
if (linphone_proxy_config_compute_publish_params_hash(cfg)) {
ms_message("Publish params have changed on proxy config [%p]",cfg);
if (cfg->long_term_event) {
if (cfg->publish) {
const char * sip_etag = linphone_event_get_custom_header(cfg->long_term_event, "SIP-ETag");
if (sip_etag) {
if (cfg->sip_etag) ms_free(cfg->sip_etag);
cfg->sip_etag = ms_strdup(sip_etag);
}
}
/*publish is terminated*/
linphone_event_terminate(cfg->long_term_event);
linphone_event_unref(cfg->long_term_event);
cfg->long_term_event = NULL;
}
if (cfg->publish) cfg->send_publish=TRUE;
} else {
ms_message("Publish params have not changed on proxy config [%p]",cfg);
}
linphone_proxy_config_write_all_to_config_file(cfg->lc);
return 0;
}
首先檢查cfg中的伺服器地址是否發生了變化;
如果變化了,將之前儲存的cfg刪除掉;如果首次登陸的話,基本跳過中間的環節;
linphone_proxy_config_write_all_to_config_file(cfg->lc); proxy.c line86
void linphone_proxy_config_write_all_to_config_file(LinphoneCore *lc){
bctbx_list_t *elem;
int i;
if (!linphone_core_ready(lc)) return;
for(elem=lc->sip_conf.proxies,i=0;elem!=NULL;elem=bctbx_list_next(elem),i++){
LinphoneProxyConfig *cfg=(LinphoneProxyConfig*)elem->data;
linphone_proxy_config_write_to_config_file(lc->config,cfg,i);
}
/*to ensure removed configs are erased:*/
linphone_proxy_config_write_to_config_file(lc->config,NULL,i);
lp_config_set_int(lc->config,"sip","default_proxy",linphone_core_get_default_proxy_config_index(lc));
}
首先遍歷lc->sip_config.proxies,裡面從儲存了剛剛add進來的cfg,並逐個寫入本地檔案中;
for迴圈執行完以後,追加寫入一個null。主要為了保證將原來刪除掉的config擦除;
linphone_proxy_config_write_to_config_file(lc->config,cfg,i) proxy.c line1059
void linphone_proxy_config_write_to_config_file(LpConfig *config, LinphoneProxyConfig *cfg, int index)
{
char key[50];
ms_message("linphone_proxy_config_write_to_config_file");
sprintf(key,"proxy_%i",index);
lp_config_clean_section(config,key);
if (cfg==NULL){
return;
}
if (cfg->type!=NULL){
lp_config_set_string(config,key,"type",cfg->type);
}
if (cfg->reg_proxy!=NULL){
lp_config_set_string(config,key,"reg_proxy",cfg->reg_proxy);
}
if (cfg->reg_route!=NULL){
lp_config_set_string(config,key,"reg_route",cfg->reg_route);
}
if (cfg->reg_identity!=NULL){
lp_config_set_string(config,key,"reg_identity",cfg->reg_identity);
}
if (cfg->realm!=NULL){
lp_config_set_string(config,key,"realm",cfg->realm);
}
if (cfg->contact_params!=NULL){
lp_config_set_string(config,key,"contact_parameters",cfg->contact_params);
}
if (cfg->contact_uri_params!=NULL){
lp_config_set_string(config,key,"contact_uri_parameters",cfg->contact_uri_params);
}
if (cfg->quality_reporting_collector!=NULL){
lp_config_set_string(config,key,"quality_reporting_collector",cfg->quality_reporting_collector);
}
lp_config_set_int(config,key,"quality_reporting_enabled",cfg->quality_reporting_enabled);
lp_config_set_int(config,key,"quality_reporting_interval",cfg->quality_reporting_interval);
lp_config_set_int(config,key,"reg_expires",cfg->expires);
lp_config_set_int(config,key,"reg_sendregister",cfg->reg_sendregister);
lp_config_set_int(config,key,"publish",cfg->publish);
lp_config_set_int(config, key, "avpf", cfg->avpf_mode);
lp_config_set_int(config, key, "avpf_rr_interval", cfg->avpf_rr_interval);
lp_config_set_int(config,key,"dial_escape_plus",cfg->dial_escape_plus);
lp_config_set_string(config,key,"dial_prefix",cfg->dial_prefix);
lp_config_set_int(config,key,"privacy",cfg->privacy);
if (cfg->refkey) lp_config_set_string(config,key,"refkey",cfg->refkey);
lp_config_set_int(config, key, "publish_expires", cfg->publish_expires);
if (cfg->nat_policy != NULL) {
lp_config_set_string(config, key, "nat_policy_ref", cfg->nat_policy->ref);
linphone_nat_policy_save_to_config(cfg->nat_policy);
}
}
這個函式主要是檢查cfg中的各種屬性,並儲存對應的值;儲存通過lp_config_set_string(xx)執行;
lp_config_set_string(config,key,”name”,”value”) 在lpconfig.c line643;
void lp_config_set_string(LpConfig *lpconfig,const char *section, const char *key, const char *value){
LpItem *item;
LpSection *sec=lp_config_find_section(lpconfig,section);
if (sec!=NULL){
item=lp_section_find_item(sec,key);
if (item!=NULL){
if (value!=NULL && value[0] != '\0')
lp_item_set_value(item,value);
else lp_section_remove_item(sec,item);
}else{
if (value!=NULL && value[0] != '\0')
lp_section_add_item(sec,lp_item_new(key,value));
}
}else if (value!=NULL && value[0] != '\0'){
sec=lp_section_new(section);
lp_config_add_section(lpconfig,sec);
lp_section_add_item(sec,lp_item_new(key,value));
}
lpconfig->modified++;
}
每個屬性值以LPSection的形式儲存,name為傳入的section;section中列表形式儲存了LPItem資料,每個LpItem以key為name;
這個函式的主要操作就是從config中找出對應的section,如果沒有就new一個,然後從section中找出key對應的item,然後設定新值;
大概資料結構 lc->config->section->item;
至此lc.addProxyConfig(prxCfg);的操作結束;
**
然後再看lc.addAuthInfo(authInfo);
**
jni介面在linphonecore_jni.cc中 line 1836
extern "C" void Java_org_linphone_core_LinphoneCoreImpl_addAuthInfo(JNIEnv* env
,jobject thiz
,jlong lc
,jlong pc) {
linphone_core_add_auth_info((LinphoneCore*)lc,(LinphoneAuthInfo*)pc);
}
真實實現在linphone_core_add_auth_info(lc,pc) authentication.c line397
void linphone_core_add_auth_info(LinphoneCore *lc, const LinphoneAuthInfo *info){
ms_message("");
LinphoneAuthInfo *ai;
bctbx_list_t *elem;
bctbx_list_t *l;
int restarted_op_count=0;
bool_t updating=FALSE;
if (info->ha1==NULL && info->passwd==NULL){
ms_warning("linphone_core_add_auth_info(): info supplied with empty password or ha1.");
}
/* find if we are attempting to modify an existing auth info */
ai=(LinphoneAuthInfo*)linphone_core_find_auth_info(lc,info->realm,info->username,info->domain);
if (ai!=NULL && ai->domain && info->domain && strcmp(ai->domain, info->domain)==0){
lc->auth_info=bctbx_list_remove(lc->auth_info,ai);
linphone_auth_info_destroy(ai);
updating=TRUE;
}
lc->auth_info=bctbx_list_append(lc->auth_info,linphone_auth_info_clone(info));
/* retry pending authentication operations */
for(l=elem=sal_get_pending_auths(lc->sal);elem!=NULL;elem=elem->next){
SalOp *op=(SalOp*)elem->data;
LinphoneAuthInfo *ai;
const SalAuthInfo *req_sai=sal_op_get_auth_requested(op);
ai=(LinphoneAuthInfo*)_linphone_core_find_auth_info(lc,req_sai->realm,req_sai->username,req_sai->domain, FALSE);
if (ai){
SalAuthInfo sai;
bctbx_list_t* proxy;
sai.username=ai->username;
sai.userid=ai->userid;
sai.realm=ai->realm;
sai.password=ai->passwd;
sai.ha1=ai->ha1;
if (ai->tls_cert && ai->tls_key) {
sal_certificates_chain_parse(&sai, ai->tls_cert, SAL_CERTIFICATE_RAW_FORMAT_PEM);
sal_signing_key_parse(&sai, ai->tls_key, "");
} else if (ai->tls_cert_path && ai->tls_key_path) {
sal_certificates_chain_parse_file(&sai, ai->tls_cert_path, SAL_CERTIFICATE_RAW_FORMAT_PEM);
sal_signing_key_parse_file(&sai, ai->tls_key_path, "");
}
/*proxy case*/
for (proxy=(bctbx_list_t*)linphone_core_get_proxy_config_list(lc);proxy!=NULL;proxy=proxy->next) {
if (proxy->data == sal_op_get_user_pointer(op)) {
linphone_proxy_config_set_state((LinphoneProxyConfig*)(proxy->data),LinphoneRegistrationProgress,"Authentication...");
break;
}
}
sal_op_authenticate(op,&sai);
restarted_op_count++;
}
}
if (l){
ms_message("linphone_core_add_auth_info(): restarted [%i] operation(s) after %s auth info for\n"
"\tusername: [%s]\n"
"\trealm [%s]\n"
"\tdomain [%s]\n",
restarted_op_count,
updating ? "updating" : "adding",
info->username ? info->username : "",
info->realm ? info->realm : "",
info->domain ? info->domain : "");
}
bctbx_list_free(l);
write_auth_infos(lc);
}
首先會檢查這個賬號是否已經存在;在linphone_core_find_auth_info()中,在line359
如果存在這個賬號的資訊,先刪除,linphone_auth_info_destroy();
然後將當期傳入的賬號資訊新增到lc中
lc->auth_info = bctbx_list_append(lc->auth_info,linphone_auth_info_clone(info));
接下來的for首次登陸的時候沒有執行,暫時沒看;
最後呼叫write_auth_infos(lc)進行儲存
writh_auth_infos();在authentication.c line377
static void write_auth_infos(LinphoneCore *lc){
bctbx_list_t *elem;
int i;
if (!linphone_core_ready(lc)) return;
if (!lc->sip_conf.save_auth_info) return;
for(elem=lc->auth_info,i=0;elem!=NULL;elem=bctbx_list_next(elem),i++){
LinphoneAuthInfo *ai=(LinphoneAuthInfo*)(elem->data);
linphone_auth_info_write_config(lc->config,ai,i);
}
linphone_auth_info_write_config(lc->config,NULL,i); /* mark the end */
}
這裡的操作與cfg的儲存比較類似,遍歷lc->auth_info,將每個LinphoneAuthInfo物件寫入本地
linphone_auth_info_write_config(lc->config,ai,i) 在line 198
void linphone_auth_info_write_config(LpConfig *config, LinphoneAuthInfo *obj, int pos) {
ms_message("linphone_auth_info_write_config");
char key[50];
bool_t store_ha1_passwd = lp_config_get_int(config, "sip", "store_ha1_passwd", 1);
sprintf(key, "auth_info_%i", pos);
lp_config_clean_section(config, key);
if (obj == NULL || lp_config_get_int(config, "sip", "store_auth_info", 1) == 0) {
return;
}
if (!obj->ha1 && obj->realm && obj->passwd && (obj->username || obj->userid) && store_ha1_passwd) {
/*compute ha1 to avoid storing clear text password*/
obj->ha1 = ms_malloc(33);
sal_auth_compute_ha1(obj->userid ? obj->userid : obj->username, obj->realm, obj->passwd, obj->ha1);
}
if (obj->username != NULL) {
lp_config_set_string(config, key, "username", obj->username);
}
if (obj->userid != NULL) {
lp_config_set_string(config, key, "userid", obj->userid);
}
if (obj->ha1 != NULL) {
lp_config_set_string(config, key, "ha1", obj->ha1);
}
if (obj->passwd != NULL) {
if (store_ha1_passwd && obj->ha1) {
/*if we have our ha1 and store_ha1_passwd set to TRUE, then drop the clear text password for security*/
linphone_auth_info_set_passwd(obj, NULL);
} else {
/*we store clear text password only if store_ha1_passwd is FALSE AND we have an ha1 to store. Otherwise, passwd would simply be removed, which might bring major auth issue*/
lp_config_set_string(config, key, "passwd", obj->passwd);
}
}
if (obj->realm != NULL) {
lp_config_set_string(config, key, "realm", obj->realm);
}
if (obj->domain != NULL) {
lp_config_set_string(config, key, "domain", obj->domain);
}
if (obj->tls_cert_path != NULL) {
lp_config_set_string(config, key, "client_cert_chain", obj->tls_cert_path);
}
if (obj->tls_key_path != NULL) {
lp_config_set_string(config, key, "client_cert_key", obj->tls_key_path);
}
}
至此待登陸的賬號資訊就儲存好了,具體的登入操作是通過lc.iterate()主迴圈實現的;
真實的註冊登入過程
linphonoe_core_iterate(lc) linphonecore.c line 2753
void linphone_core_iterate(LinphoneCore *lc){
......
sal_iterate(lc->sal);
if (lc->msevq) ms_event_queue_pump(lc->msevq);
if (lc->auto_net_state_mon) monitor_network_state(lc, current_real_time);
proxy_update(lc);
/////...
}
這是linphone的核心功能,在這個迴圈內,主要進行的操作有,接收sip訊息、處理定時器、處理proxy的註冊狀態變化、認證重連;
關於登入的處理是通過proxy_update(lc)實現的
proxy_update(lc) 在linphonecore.c line2654
static void proxy_update(LinphoneCore *lc){
//ms_message("proxy_update");
bctbx_list_t *elem,*next;
bctbx_list_for_each(lc->sip_conf.proxies,(void (*)(void*))&linphone_proxy_config_update);
for(elem=lc->sip_conf.deleted_proxies;elem!=NULL;elem=next){
LinphoneProxyConfig* cfg = (LinphoneProxyConfig*)elem->data;
next=elem->next;
if (ms_time(NULL) - cfg->deletion_date > 32) {
lc->sip_conf.deleted_proxies =bctbx_list_erase_link(lc->sip_conf.deleted_proxies,elem);
ms_message("Proxy config for [%s] is definitely removed from core.",linphone_proxy_config_get_addr(cfg));
_linphone_proxy_config_release_ops(cfg);
linphone_proxy_config_unref(cfg);
}
}
}
函式內通過bctbx_list_for_each(lc->sip_conf.proxies,(xxxx)&linphone_proxy_config_update)
來呼叫linphone_proxy_config_update檢查lc->sip_conf.poxies中的每個config;
linphone_proxy_config_update(LinphoneProxyConfig *cfg); proxy.c line1241
void linphone_proxy_config_update(LinphoneProxyConfig *cfg){
//ms_message("linphone_proxy_config_update");
LinphoneCore *lc=cfg->lc;
if (cfg->commit){
ms_message("update cfg->commit = true");
if (cfg->type && cfg->ssctx==NULL){
linphone_proxy_config_activate_sip_setup(cfg);
}
if (can_register(cfg)){
linphone_proxy_config_register(cfg);
cfg->commit=FALSE;
}
}
if (cfg->send_publish && (cfg->state==LinphoneRegistrationOk || cfg->state==LinphoneRegistrationCleared)){
linphone_proxy_config_send_publish(cfg,lc->presence_model);
cfg->send_publish=FALSE;
}
}
在之前addProxyConfig的時候,cfg->commit為true;然後判斷can_register(cfg),
can_register(cfg) proxy.c line1224
static bool_t can_register(LinphoneProxyConfig *cfg){
LinphoneCore *lc=cfg->lc;
#ifdef BUILD_UPNP
if (linphone_core_get_firewall_policy(lc)==LinphonePolicyUseUpnp){
if(lc->sip_conf.register_only_when_upnp_is_ok &&
(lc->upnp == NULL || !linphone_upnp_context_is_ready_for_register(lc->upnp))) {
return FALSE;
}
}
#endif //BUILD_UPNP
if (lc->sip_conf.register_only_when_network_is_up){
return lc->sip_network_reachable;
}
return TRUE;
}
檢查防火牆是否可用啥的,然後執行註冊
linphone_proxy_config_register(cfg) proxy.c line462
static void linphone_proxy_config_register(LinphoneProxyConfig *cfg){
ms_message("linphone_proxy_config_register");
if (cfg->reg_sendregister){
LinphoneAddress* proxy=linphone_address_new(cfg->reg_proxy);
char* proxy_string;
char * from = linphone_address_as_string(cfg->identity_address);
LinphoneAddress *contact;
ms_message("LinphoneProxyConfig [%p] about to register (LinphoneCore version: %s) from =[ %s ]" ,cfg,linphone_core_get_version() , from);
proxy_string=linphone_address_as_string_uri_only(proxy);
linphone_address_destroy(proxy);
if (cfg->op)
sal_op_release(cfg->op);
cfg->op=sal_op_new(cfg->lc->sal);
linphone_configure_op(cfg->lc, cfg->op, cfg->identity_address, cfg->sent_headers, FALSE);
if ((contact=guess_contact_for_register(cfg))) {
sal_op_set_contact_address(cfg->op,contact);
linphone_address_destroy(contact);
}
sal_op_set_user_pointer(cfg->op,cfg);
if (sal_register(cfg->op,proxy_string, cfg->reg_identity, cfg->expires, cfg->pending_contact)==0) {
if (cfg->pending_contact) {
linphone_address_unref(cfg->pending_contact);
cfg->pending_contact=NULL;
}
linphone_proxy_config_set_state(cfg,LinphoneRegistrationProgress,"Registration in progress");
} else {
linphone_proxy_config_set_state(cfg,LinphoneRegistrationFailed,"Registration failed");
}
ms_free(proxy_string);
ms_free(from);
} else {
/* unregister if registered*/
if (cfg->state == LinphoneRegistrationProgress) {
linphone_proxy_config_set_state(cfg,LinphoneRegistrationCleared,"Registration cleared");
}
_linphone_proxy_config_unregister(cfg);
}
}
先將cfg原來的op刪除,然後執行linphone_configure_op(xxx);
linphone_configure_op(cfg->lc,cfg->op,cfg->identity_address,cfg->sent_headers,false); linphonecore.c line3283
void linphone_configure_op(LinphoneCore *lc, SalOp *op, const LinphoneAddress *dest, SalCustomHeader *headers, bool_t with_contact){
bctbx_list_t *routes=NULL;
LinphoneProxyConfig *proxy=linphone_core_lookup_known_proxy(lc,dest);
const char *identity;
if (proxy){
identity=linphone_proxy_config_get_identity(proxy);
if (linphone_proxy_config_get_privacy(proxy)!=LinphonePrivacyDefault) {
sal_op_set_privacy(op,linphone_proxy_config_get_privacy(proxy));
}
}else identity=linphone_core_get_primary_contact(lc);
/*sending out of calls*/
if (proxy){
routes=make_routes_for_proxy(proxy,dest);
linphone_transfer_routes_to_op(routes,op);
}
sal_op_set_to_address(op,dest);
sal_op_set_from(op,identity);
sal_op_set_sent_custom_header(op,headers);
sal_op_set_realm(op,linphone_proxy_config_get_realm(proxy));
if (with_contact && proxy && proxy->op){
const SalAddress *contact;
if ((contact=sal_op_get_contact_address(proxy->op))){
SalTransport tport=sal_address_get_transport((SalAddress*)contact);
SalAddress *new_contact=sal_address_clone(contact);
sal_address_clean(new_contact); /* clean out contact_params that come from proxy config*/
sal_address_set_transport(new_contact,tport);
sal_op_set_contact_address(op,new_contact);
sal_address_destroy(new_contact);
}
}
sal_op_cnx_ip_to_0000_if_sendonly_enable(op,lp_config_get_default_int(lc->config,"sip","cnx_ip_to_0000_if_sendonly_enabled",0)); /*also set in linphone_call_new_incoming*/
}
這個介面主要是講op原有的屬性清除,然後設定為新的屬性
sal_op_set_to_address(op,dest); 給op設定目標地址
sal_op_set_from(op,identity); 給op設定本地地址
sal_op_set_sent_custom_header(op,headers); 給op設定sip訊息頭
sal_op_set_realm(op,linphone_proxy_config_get_realm(proxy)); 給op設定realm;
sal_op_set_sent_custom_header(op,headers); sal_op_impl.c line700;
void sal_op_set_sent_custom_header(SalOp *op, SalCustomHeader* ch){
SalOpBase *b=(SalOpBase *)op;
if (b->sent_custom_headers){
sal_custom_header_free(b->sent_custom_headers);
b->sent_custom_headers=NULL;
}
if (ch) belle_sip_object_ref((belle_sip_message_t*)ch);
b->sent_custom_headers=ch;
}
先刪除op原來的的sent_custom_headers,然後重新設定為ch;
回到linphone_proxy_config_register中,繼續執行sal_register();
sal_register(); sal_op_registration.c line78
int sal_register(SalOp *op, const char *proxy, const char *from, int expires,SalAddress* old_contact){
belle_sip_message("sal_register %s" ,from);
belle_sip_request_t *req;
belle_sip_uri_t* req_uri;
belle_sip_header_t* accept_header;
if (op->refresher){
belle_sip_refresher_stop(op->refresher);
belle_sip_object_unref(op->refresher);
op->refresher=NULL;
}
op->type=SalOpRegister;
sal_op_set_from(op,from);
sal_op_set_to(op,from);
sal_op_set_route(op,proxy);
req = sal_op_build_request(op,"REGISTER");
req_uri = belle_sip_request_get_uri(req);
belle_sip_uri_set_user(req_uri,NULL); /*remove userinfo if any*/
if (op->base.root->use_dates){
time_t curtime=time(NULL);
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req),BELLE_SIP_HEADER(belle_sip_header_date_create_from_time(&curtime)));
}
accept_header = belle_sip_header_create("Accept", "application/sdp, text/plain, application/vnd.gsma.rcs-ft-http+xml");
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), accept_header);
belle_sip_message_set_header(BELLE_SIP_MESSAGE(req),(belle_sip_header_t*)sal_op_create_contact(op));
belle_sip_list_t* new_list = belle_sip_message_get_all_headers(BELLE_SIP_MESSAGE(req));
belle_sip_list_t* iterator = new_list;
for (;iterator!=NULL;iterator=iterator->next) {
belle_sip_header_t* header=(belle_sip_header_t*)iterator->data;
char* header_string=belle_sip_object_to_string(header);
belle_sip_message("header = %s",header_string);
}
if (old_contact) {
belle_sip_header_contact_t *contact=belle_sip_header_contact_create((const belle_sip_header_address_t *)old_contact);
if (contact) {
char * tmp;
belle_sip_header_contact_set_expires(contact,0); /*remove old aor*/
belle_sip_message_add_header(BELLE_SIP_MESSAGE(req), BELLE_SIP_HEADER(contact));
tmp = belle_sip_object_to_string(contact);
ms_message("Clearing contact [%s] for op [%p]",tmp,op);
ms_free(tmp);
} else {
ms_error("Cannot add old contact header to op [%p]",op);
}
}
return sal_op_send_and_create_refresher(op,req,expires,register_refresher_listener);
}
首先構建sdp的request,sal_op_build_request(op,”REGISTER”);
sal_op_build_request() sal_op_impl.c line155;
這個函式主要是構建request的基本資訊;
然後繼續在request填充cantact資訊;
最後返回 sal_op_send_and_create_refresher();
sal_op_send_and_create_refresher(); sal_op_impl.c line 663
int sal_op_send_and_create_refresher(SalOp* op,belle_sip_request_t* req, int expires,belle_sip_refresher_listener_t listener ) {
if (sal_op_send_request_with_expires(op,req,expires)==0) {
if (op->refresher) {
belle_sip_refresher_stop(op->refresher);
belle_sip_object_unref(op->refresher);
}
if ((op->refresher = belle_sip_client_transaction_create_refresher(op->pending_client_trans))) {
/*since refresher acquires the transaction, we should remove our context from the transaction, because we won't be notified
* that it is terminated anymore.*/
sal_op_unref(op);/*loose the reference that was given to the transaction when creating it*/
/* Note that the refresher will replace our data with belle_sip_transaction_set_application_data().
Something in the design is not very good here, it makes things complicated to the belle-sip user.
Possible ideas to improve things: refresher shall not use belle_sip_transaction_set_application_data() internally, refresher should let the first transaction
notify the user as a normal transaction*/
belle_sip_refresher_set_listener(op->refresher,listener,op);
belle_sip_refresher_set_retry_after(op->refresher,op->base.root->refresher_retry_after);
belle_sip_refresher_set_realm(op->refresher,op->base.realm);
belle_sip_refresher_enable_manual_mode(op->refresher,op->manual_refresher);
return 0;
} else {
return -1;
}
}
return -1;
}
第一步 執行sal_op_send_request_with_expires();
sal_op_send_request_with_expires(), sal_op_impl.c line263
int sal_op_send_request_with_expires(SalOp* op, belle_sip_request_t* request,int expires) {
belle_sip_message("sal_op_send_request_with_expires");
belle_sip_header_expires_t* expires_header=(belle_sip_header_expires_t*)belle_sip_message_get_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_EXPIRES);
if (!expires_header && expires>=0) {
belle_sip_message_add_header(BELLE_SIP_MESSAGE(request),BELLE_SIP_HEADER(expires_header=belle_sip_header_expires_new()));
}
if (expires_header) belle_sip_header_expires_set_expires(expires_header,expires);
return sal_op_send_request(op,request);
}
繼續在request中增加expires的頭欄位,然後返回sal_op_send_request;
sal_op_send_request(); sal_op_impl.c line399
int sal_op_send_request(SalOp* op, belle_sip_request_t* request) {
bool_t need_contact=FALSE;
if (request==NULL) {
return -1; /*sanity check*/
}
if (strcmp(belle_sip_request_get_method(request),"INVITE")==0
||strcmp(belle_sip_request_get_method(request),"REGISTER")==0
||strcmp(belle_sip_request_get_method(request),"SUBSCRIBE")==0
||strcmp(belle_sip_request_get_method(request),
相關推薦
Linphone-android 登入過程增加自定義訊息頭流程分析
註冊增加訊息頭
在saveNewAccount()中;
新增自定義訊息頭
LinphoneProxyConfig prxCfg = lc.createProxyConfig(identityAddr.asString(), proxyAddr.asSt
接入微信公眾平臺開發之使用者關注(取消)事件觸發後臺自定義訊息體通知給使用者的實現過程
1.需求:使用者關注公眾號後回覆給使用者一個字串,字串不能重複使用即如果a使用者關注公眾號後商戶後臺回覆給使用者字串str1後,b使用者關注就是其他字串,且a使用者取消關注再次關注不回覆訊息體
2.實現過程:
①首先配置伺服器url並開啟,再次過程中需要微信後臺與商戶後臺進行通訊,所以,微信後臺會發送
MFC中自定義訊息過程
這幾天接觸一個MFC自定義訊息,但是之前沒用過,犯了一個低階錯誤,粘貼出來供大家參考。
下圖是Windows訊息圖示:
有使用者自定義訊息的一些範圍。以一個具體的例子介紹使用方法:
第一步:本人定義一個訊息
 
Android Camera增加自定義影象處理並錄製MP4
在我的一篇部落格Android Camera API/Camera2 API 相機預覽及濾鏡、貼紙等處理中,介紹瞭如何給相機增加濾鏡貼紙的方法,也就是自定義影象處理。而另外一篇部落格Android硬編碼——音訊編碼、視訊編碼及音視訊混合介紹了一種編碼錄製MP4的
Android 環信擴充套件訊息(自定義訊息)
之前整合即時通訊(環信)的時候,需要用到自定義訊息的功能。而在開發過程中遇到了許多的問題,之前工作比較忙,現在有時間了記錄一下。
第一步:
首先,在聊天介面新增一個傳送擴充套件訊息的MenuItem
Android 使用極光推送訊息詳細介紹之自定義訊息
前言
上一篇文章,我們詳細介紹了下極光推送的使用,不過還是差一點,那就是自定義訊息的使用.這一篇,我們將慢慢來熟悉極光推送的自定義訊息的使用,附上本人的上一篇文章,有興趣的可以去看看:
http://blog.csdn.net/greathfs/articl
Android 使用極光推送自定義訊息打造個性的訊息推送效果
極光推送,是一個面向普通開發者開放的,免費的第三方訊息推送服務。本篇部落格將結合案例介紹極光推送自定義訊息的使用方法,利用自定義訊息實現專案中特定的訊息推送需求。
本案例將實現如圖效果:
參考官方Android SDK 教程完成鐳射推送的基本配置
區
向android中增加自定義的Linux核心啟動引數
前言,android裝置中常常需要新增自定義的核心配置,如imx51的primary_di定義 pmem定義等,這時需要使用__setup函式,下面的文章中詳述了該過程。
轉:如何增加自定義的Linux核心啟動引數
在驅動開發的過程中,有時為了除錯方便,需要給驅動傳入引數。
android:如何通過自定義工程模板讓新建的工程都默認支持lambda表達式
wan tro idt ref height 代碼 spa span oid 首先參考這篇文章:自定義Android Studio工程模板,了解如何自定義模板然後結合我們上一篇文章 android: 在android studio中使用retrolambda的步驟的要點,修
spring中增加自定義配置支持
控制 images 映射 獲取 path efi ade get 處理 spring.schemas
在使用spring時,我們會首先編寫spring的配置文件,在配置文件中,我們除了使用基本的命名空間http://www.springframework.org/schem
存儲過程與自定義函數的區別
.cn logs -1 定義 img ima 自定義 image cnblogs
存儲過程與自定義函數的區別
Android編程入門--自定義Application
如何 使用 shu ppc android系統 contex target logs @override Android系統自動會為每個程序運行時創建一個Application類的對象且只創建一個
參考博客:Android 當中 application的使用
參考文章:
Asp.net Identity 修改默認數據庫,增加自定義字段
擴展 studio required ssa 字段 profile 服務器 cat fix visual studio 2013
先新建一個項目
選擇MVC,確定
打開 Views\Shared\_Layout.cshtml文件,按自己的要求修改
改
[ht
MySQL存儲過程/存儲過程與自定義函數的區別
toolbar 基本 value reg pan lba tin 進行 只為 ---------------------------存儲過程--------------------
語法:
創建存儲過程:
CREATE [definer = {user|current
存儲過程和自定義函數的區別
tab idt 函數 操作 ron 執行 pan span tex
存儲過程
自定義函數
功能復雜
針對性強
對表操作
多個返回值
一個返回值
獨立執行
可以作為其他SQL語句的組成部分出現
存儲過程和自定義函數的區別
Android 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉刷新
樣式 post and 微信 修改 size roi 自定義 details 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉刷新Android 修改源碼自定義SwipeRefreshLayout樣式——高仿微信朋友圈下拉
.net core Identity集成IdentityServer(2) 實現IprofileService接口在accesstoken中增加自定義claims
實現 ets gen 配置 授權 spn cor devel color 導讀1. 如何添加自定義的claims.前請提要目前我們擁有了三個web應用.localhost:40010, 驗證服務器 localhost:40011, mvc客戶端, 充當webapp請求者 l
Android中快速實現自定義字體!
sdk true fcm version ttf spa pre ets 怎麽 前言:我們都知道,Android中默認的字體是黑體,而大多數app也都是使用的這種字體,但我們發現,大多數app中,個別地方字體非常好看,例如app的標題欄,菜單欄等地方,那他們是怎麽做到的呢?
自定義訊息提示框
使用原生JavaScript簡單封裝的一個訊息提示模態框,如果誰有更好的方式可以分享,謝謝!
<!DOCTYPE html>
<html lang="en">
<head>
<title></title>
<meta
如何在MFC中自定義訊息
訊息對映、迴圈機制是Windows程式執行的基本方式。VC++ MFC 中有許多現成的訊息控制代碼,可當我們需要完成其它的任務,需要自定義訊息,就遇到了一些困難。在MFC ClassWizard中不允許新增使用者自定義訊息,所以我們必須在程式中新增相應程式碼