📄 pjsua_pres.c
字号:
param->reason.ptr)); } pjsip_publishc_destroy(param->pubc); acc->publish_sess = NULL; }}/* * Send PUBLISH request. */static pj_status_t send_publish(int acc_id, pj_bool_t active){ pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsip_pres_status pres_status; pjsip_tx_data *tdata; pj_status_t status; /* Create PUBLISH request */ if (active) { char *bpos; pj_str_t entity; status = pjsip_publishc_publish(acc->publish_sess, PJ_TRUE, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } /* 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; /* Be careful not to send PIDF with presence entity ID containing * "<" character. */ if ((bpos=pj_strchr(&acc_cfg->id, '<')) != NULL) { char *epos = pj_strchr(&acc_cfg->id, '>'); if (epos - bpos < 2) { pj_assert(!"Unexpected invalid URI"); status = PJSIP_EINVALIDURI; goto on_error; } entity.ptr = bpos+1; entity.slen = epos - bpos - 1; } else { entity = acc_cfg->id; } /* Create and add PIDF message body */ status = pjsip_pres_create_pidf(tdata->pool, &pres_status, &entity, &tdata->msg->body); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PIDF for PUBLISH request", status); pjsip_tx_data_dec_ref(tdata); goto on_error; } } else { status = pjsip_publishc_unpublish(acc->publish_sess, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error creating PUBLISH request", status); goto on_error; } } /* Add headers etc */ pjsua_process_msg_data(tdata, NULL); /* Send the PUBLISH request */ status = pjsip_publishc_send(acc->publish_sess, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error sending PUBLISH request", status); goto on_error; } acc->publish_state = acc->online_status; return PJ_SUCCESS;on_error: pjsip_publishc_destroy(acc->publish_sess); acc->publish_sess = NULL; return status;}/* Create client publish session */pj_status_t pjsua_pres_init_publish_acc(int acc_id){ const pj_str_t STR_PRESENCE = { "presence", 8 }; pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_acc *acc = &pjsua_var.acc[acc_id]; pj_status_t status; /* Create and init client publication session */ if (acc_cfg->publish_enabled) { /* Create client publication */ status = pjsip_publishc_create(pjsua_var.endpt, 0, acc, &publish_cb, &acc->publish_sess); if (status != PJ_SUCCESS) { acc->publish_sess = NULL; return status; } /* Initialize client publication */ status = pjsip_publishc_init(acc->publish_sess, &STR_PRESENCE, &acc_cfg->id, &acc_cfg->id, &acc_cfg->id, 60); if (status != PJ_SUCCESS) { acc->publish_sess = NULL; return status; } /* Add credential for authentication */ pjsip_publishc_set_credentials(acc->publish_sess, acc->cred_cnt, acc->cred); /* Set route-set */ pjsip_publishc_set_route_set(acc->publish_sess, &acc->route_set); /* Send initial PUBLISH request */ if (acc->online_status != 0) { status = send_publish(acc_id, PJ_TRUE); if (status != PJ_SUCCESS) return status; } } else { acc->publish_sess = NULL; } return PJ_SUCCESS;}/* Init presence for account */pj_status_t pjsua_pres_init_acc(int acc_id){ pjsua_acc *acc = &pjsua_var.acc[acc_id]; /* Init presence subscription */ pj_list_init(&acc->pres_srv_list); return PJ_SUCCESS;}/* Terminate server subscription for the account */void pjsua_pres_delete_acc(int acc_id){ pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_srv_pres *uapres; uapres = pjsua_var.acc[acc_id].pres_srv_list.next; /* Notify all subscribers that we're no longer available */ while (uapres != &acc->pres_srv_list) { pjsip_pres_status pres_status; pj_str_t reason = { "noresource", 10 }; pjsip_tx_data *tdata; pjsip_pres_get_status(uapres->sub, &pres_status); pres_status.info[0].basic_open = pjsua_var.acc[acc_id].online_status; pjsip_pres_set_status(uapres->sub, &pres_status); if (pjsip_pres_notify(uapres->sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &reason, &tdata)==PJ_SUCCESS) { pjsip_pres_send_request(uapres->sub, tdata); } uapres = uapres->next; } /* Clear server presence subscription list because account might be reused * later. */ pj_list_init(&acc->pres_srv_list); /* Terminate presence publication, if any */ if (acc->publish_sess) { acc->online_status = PJ_FALSE; send_publish(acc_id, PJ_FALSE); if (acc->publish_sess) { pjsip_publishc_destroy(acc->publish_sess); acc->publish_sess = NULL; } acc_cfg->publish_enabled = PJ_FALSE; }}/* Refresh subscription (e.g. when our online status has changed) */static void refresh_server_subscription(int acc_id){ pjsua_acc *acc = &pjsua_var.acc[acc_id]; pjsua_acc_config *acc_cfg = &pjsua_var.acc[acc_id].cfg; pjsua_srv_pres *uapres; uapres = pjsua_var.acc[acc_id].pres_srv_list.next; while (uapres != &acc->pres_srv_list) { pjsip_pres_status pres_status; pjsip_tx_data *tdata; pjsip_pres_get_status(uapres->sub, &pres_status); if (pres_status.info[0].basic_open != acc->online_status) { pres_status.info[0].basic_open = acc->online_status; pjsip_pres_set_status(uapres->sub, &pres_status); if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS) { pjsua_process_msg_data(tdata, NULL); pjsip_pres_send_request(uapres->sub, tdata); } } uapres = uapres->next; } /* Send PUBLISH if required. We only do this when we have a PUBLISH * session. If we don't have a PUBLISH session, then it could be * that we're waiting until registration has completed before we * send the first PUBLISH. */ if (acc_cfg->publish_enabled && acc->publish_sess) { if (acc->publish_state != acc->online_status) { send_publish(acc_id, PJ_TRUE); } }}/*************************************************************************** * Client subscription. *//* 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 = pjsip_evsub_get_mod_data(sub, pjsua_var.mod.id); if (buddy) { PJ_LOG(3,(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) { buddy->sub = NULL; buddy->status.info_cnt = 0; pjsip_evsub_set_mod_data(sub, pjsua_var.mod.id, NULL); } /* Call callback */ if (pjsua_var.ua_cfg.cb.on_buddy_state) (*pjsua_var.ua_cfg.cb.on_buddy_state)(buddy->index); } PJSUA_UNLOCK();}/* 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 = 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_subscribe_method)!=0) { PJSUA_UNLOCK(); return; } /* Find contact header. */ 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 = pj_pool_alloc(pjsua_var.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();}/* 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 = 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();}/* Event subscription callback. */static pjsip_evsub_user pres_callback = { &pjsua_evsub_on_state, &pjsua_evsub_on_tsx_state, NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless * we want to authenticate */ &pjsua_evsub_on_rx_notify, NULL, /* on_client_refresh: Use default behaviour, which is to * refresh client subscription. */ NULL, /* on_server_timeout: Use default behaviour, which is to send * NOTIFY to terminate. */};/* It does what it says.. */static void subscribe_buddy_presence(unsigned index){ pjsua_buddy *buddy; int acc_id; pjsua_acc *acc; pj_str_t contact; pjsip_dialog *dlg; pjsip_tx_data *tdata; pj_status_t status; buddy = &pjsua_var.buddy[index]; acc_id = pjsua_acc_find_for_outgoing(&buddy->uri); acc = &pjsua_var.acc[acc_id]; PJ_LOG(4,(THIS_FILE, "Using account %d for buddy %d subscription", acc_id, index)); /* Generate suitable Contact header */ status = pjsua_acc_create_uac_contact(pjsua_var.pool, &contact, acc_id, &buddy->uri); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to generate Contact header", status); return; } /* Create UAC dialog */ status = pjsip_dlg_create_uac( pjsip_ua_instance(), &acc->cfg.id, &contact, &buddy->uri, NULL, &dlg); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create dialog", status); return; } status = pjsip_pres_create_uac( dlg, &pres_callback, PJSIP_EVSUB_NO_EVENT_ID, &buddy->sub); if (status != PJ_SUCCESS) { pjsua_var.buddy[index].sub = NULL; pjsua_perror(THIS_FILE, "Unable to create presence client", status); pjsip_dlg_terminate(dlg); return; } /* 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); } /* Set route-set */ if (!pj_list_empty(&acc->route_set)) { pjsip_dlg_set_route_set(dlg, &acc->route_set); } /* Set credentials */ if (acc->cred_cnt) { pjsip_auth_clt_set_credentials( &dlg->auth_sess, acc->cred_cnt, acc->cred); } pjsip_evsub_set_mod_data(buddy->sub, pjsua_var.mod.id, buddy); status = pjsip_pres_initiate(buddy->sub, -1, &tdata); if (status != PJ_SUCCESS) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to create initial SUBSCRIBE", status); return; } pjsua_process_msg_data(tdata, NULL); status = pjsip_pres_send_request(buddy->sub, tdata); if (status != PJ_SUCCESS) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to send initial SUBSCRIBE", status); return; }}/* It does what it says... */static void unsubscribe_buddy_presence(unsigned index){ pjsua_buddy *buddy; pjsip_tx_data *tdata; pj_status_t status; buddy = &pjsua_var.buddy[index]; if (buddy->sub == NULL) return; if (pjsip_evsub_get_state(buddy->sub) == PJSIP_EVSUB_STATE_TERMINATED) { pjsua_var.buddy[index].sub = NULL; return; } status = pjsip_pres_initiate( buddy->sub, 0, &tdata); if (status == PJ_SUCCESS) { pjsua_process_msg_data(tdata, NULL); status = pjsip_pres_send_request( buddy->sub, tdata ); } if (status != PJ_SUCCESS) { pjsip_pres_terminate(buddy->sub, PJ_FALSE); buddy->sub = NULL; pjsua_perror(THIS_FILE, "Unable to unsubscribe presence", status); }}/* It does what it says.. */static void refresh_client_subscriptions(void){ unsigned i; for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { if (!pjsua_var.buddy[i].uri.slen) continue; if (pjsua_var.buddy[i].monitor && !pjsua_var.buddy[i].sub) { subscribe_buddy_presence(i); } else if (!pjsua_var.buddy[i].monitor && pjsua_var.buddy[i].sub) { unsubscribe_buddy_presence(i); } }}/* * Init presence */pj_status_t pjsua_pres_init(){ unsigned i; pj_status_t status; status = pjsip_endpt_register_module( pjsua_var.endpt, &mod_pjsua_pres); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to register pjsua presence module", status); } for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { reset_buddy(i); } return status;}/* * Start presence subsystem. */pj_status_t pjsua_pres_start(void){ /* Nothing to do (is it?) */ return PJ_SUCCESS;}/* * Refresh presence subscriptions */void pjsua_pres_refresh(){ unsigned i; refresh_client_subscriptions(); for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { if (pjsua_var.acc[i].valid) refresh_server_subscription(i); }}/* * Shutdown presence. */void pjsua_pres_shutdown(void){ unsigned i; for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i) { if (!pjsua_var.acc[i].valid) continue; pjsua_pres_delete_acc(i); } for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.buddy); ++i) { pjsua_var.buddy[i].monitor = 0; } pjsua_pres_refresh();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -