PJ_DEF(pj_xml_node*) pj_xml_clone( pj_pool_t *pool, const pj_xml_node *rhs) { pj_xml_node *node; const pj_xml_attr *r_attr; const pj_xml_node *child; node = alloc_node(pool); pj_strdup(pool, &node->name, &rhs->name); pj_strdup(pool, &node->content, &rhs->content); /* Clone all attributes */ r_attr = rhs->attr_head.next; while (r_attr != &rhs->attr_head) { pj_xml_attr *attr; attr = alloc_attr(pool); pj_strdup(pool, &attr->name, &r_attr->name); pj_strdup(pool, &attr->value, &r_attr->value); pj_list_push_back(&node->attr_head, attr); r_attr = r_attr->next; } /* Clone all child nodes. */ child = rhs->node_head.next; while (child != (pj_xml_node*) &rhs->node_head) { pj_xml_node *new_child; new_child = pj_xml_clone(pool, child); pj_list_push_back(&node->node_head, new_child); child = child->next; } return node; }
/* * Reset the echo canceller. */ PJ_DEF(pj_status_t) pjmedia_echo_reset(pjmedia_echo_state *echo ) { while (!pj_list_empty(&echo->lat_buf)) { struct frame *frm; frm = echo->lat_buf.next; pj_list_erase(frm); pj_list_push_back(&echo->lat_free, frm); } echo->lat_ready = PJ_FALSE; pjmedia_delay_buf_reset(echo->delay_buf); echo->op->ec_reset(echo->state); return PJ_SUCCESS; }
static void* multipart_clone_data(pj_pool_t *pool, const void *data, unsigned len) { const struct multipart_data *src; struct multipart_data *dst; const pjsip_multipart_part *src_part; PJ_UNUSED_ARG(len); src = (const struct multipart_data*) data; dst = PJ_POOL_ALLOC_T(pool, struct multipart_data); pj_list_init(&dst->part_head); pj_strdup(pool, &dst->boundary, &src->boundary); src_part = src->part_head.next; while (src_part != &src->part_head) { pjsip_multipart_part *dst_part; const pjsip_hdr *src_hdr; dst_part = pjsip_multipart_create_part(pool); src_hdr = src_part->hdr.next; while (src_hdr != &src_part->hdr) { pjsip_hdr *dst_hdr = (pjsip_hdr*)pjsip_hdr_clone(pool, src_hdr); pj_list_push_back(&dst_part->hdr, dst_hdr); src_hdr = src_hdr->next; } dst_part->body = pjsip_msg_body_clone(pool, src_part->body); pj_list_push_back(&dst->part_head, dst_part); src_part = src_part->next; } return (void*)dst; }
static void ui_call_transfer(pj_bool_t no_refersub) { if (current_call == -1) { PJ_LOG(3,(THIS_FILE, "No current call")); } else { int call = current_call; char buf[128]; pjsip_generic_string_hdr refer_sub; pj_str_t STR_REFER_SUB = { "Refer-Sub", 9 }; pj_str_t STR_FALSE = { "false", 5 }; pjsua_call_info ci; input_result result; pjsua_msg_data msg_data; pjsua_call_get_info(current_call, &ci); printf("Transferring current call [%d] %.*s\n", current_call, (int)ci.remote_info.slen, ci.remote_info.ptr); ui_input_url("Transfer to URL", buf, sizeof(buf), &result); /* Check if call is still there. */ if (call != current_call) { puts("Call has been disconnected"); return; } pjsua_msg_data_init(&msg_data); if (no_refersub) { /* Add Refer-Sub: false in outgoing REFER request */ pjsip_generic_string_hdr_init2(&refer_sub, &STR_REFER_SUB, &STR_FALSE); pj_list_push_back(&msg_data.hdr_list, &refer_sub); } if (result.nb_result != PJSUA_APP_NO_NB) { if (result.nb_result == -1 || result.nb_result == 0) puts("You can't do that with transfer call!"); else { pjsua_buddy_info binfo; pjsua_buddy_get_info(result.nb_result-1, &binfo); pjsua_call_xfer( current_call, &binfo.uri, &msg_data); } } else if (result.uri_result) { pj_str_t tmp; tmp = pj_str(result.uri_result); pjsua_call_xfer( current_call, &tmp, &msg_data); } } }
/* * Create an empty multipart body. */ PJ_DEF(pjsip_msg_body*) pjsip_multipart_create( pj_pool_t *pool, const pjsip_media_type *ctype, const pj_str_t *boundary) { pjsip_msg_body *body; pjsip_param *ctype_param; struct multipart_data *mp_data; pj_str_t STR_BOUNDARY = { "boundary", 8 }; PJ_ASSERT_RETURN(pool, NULL); body = PJ_POOL_ZALLOC_T(pool, pjsip_msg_body); /* content-type */ if (ctype && ctype->type.slen) { pjsip_media_type_cp(pool, &body->content_type, ctype); } else { pj_str_t STR_MULTIPART = {"multipart", 9}; pj_str_t STR_MIXED = { "mixed", 5 }; pjsip_media_type_init(&body->content_type, &STR_MULTIPART, &STR_MIXED); } /* multipart data */ mp_data = PJ_POOL_ZALLOC_T(pool, struct multipart_data); pj_list_init(&mp_data->part_head); if (boundary) { pj_strdup(pool, &mp_data->boundary, boundary); } else { pj_create_unique_string(pool, &mp_data->boundary); } body->data = mp_data; /* Add ";boundary" parameter to content_type parameter. */ ctype_param = pjsip_param_find(&body->content_type.param, &STR_BOUNDARY); if (!ctype_param) { ctype_param = PJ_POOL_ALLOC_T(pool, pjsip_param); ctype_param->name = STR_BOUNDARY; pj_list_push_back(&body->content_type.param, ctype_param); } ctype_param->value = mp_data->boundary; /* function pointers */ body->print_body = &multipart_print_body; body->clone_data = &multipart_clone_data; return body; }
static void init_media_type(pjsip_media_type *mt, char *type, char *subtype, char *boundary) { static pjsip_param prm; pjsip_media_type_init(mt, NULL, NULL); if (type) mt->type = pj_str(type); if (subtype) mt->subtype = pj_str(subtype); if (boundary) { pj_list_init(&prm); prm.name = pj_str("boundary"); prm.value = pj_str(boundary); pj_list_push_back(&mt->param, &prm); } }
/* * Let the Echo Canceller know that a frame has been played to the speaker. */ PJ_DEF(pj_status_t) pjmedia_echo_playback( pjmedia_echo_state *echo, pj_int16_t *play_frm ) { /* If EC algo has playback handler, just pass the frame. */ if (echo->op->ec_playback) { return (*echo->op->ec_playback)(echo->state, play_frm); } /* Playing frame should be stored, as it will be used by echo_capture() * as reference frame, delay buffer is used for storing the playing frames * as in case there was clock drift between mic & speaker. * * Ticket #830: * Note that pjmedia_delay_buf_put() may modify the input frame and those * modified frames may not be smooth, i.e: if there were two or more * consecutive pjmedia_delay_buf_get() before next pjmedia_delay_buf_put(), * so we'll just feed the delay buffer with the copy of playing frame, * instead of the original playing frame. However this will cause the EC * uses slight 'different' frames (for reference) than actually played * by the speaker. */ pjmedia_copy_samples(echo->frm_buf, play_frm, echo->samples_per_frame); pjmedia_delay_buf_put(echo->delay_buf, echo->frm_buf); if (!echo->lat_ready) { /* We've not built enough latency in the buffer, so put this frame * in the latency buffer list. */ struct frame *frm; if (pj_list_empty(&echo->lat_free)) { echo->lat_ready = PJ_TRUE; PJ_LOG(5,(echo->obj_name, "Latency bufferring complete")); return PJ_SUCCESS; } frm = echo->lat_free.prev; pj_list_erase(frm); /* Move one frame from delay buffer to the latency buffer. */ pjmedia_delay_buf_get(echo->delay_buf, echo->frm_buf); pjmedia_copy_samples(frm->buf, echo->frm_buf, echo->samples_per_frame); pj_list_push_back(&echo->lat_buf, frm); } return PJ_SUCCESS; }
/* * Let the Echo Canceller know that a frame has been played to the speaker. */ pj_status_t pjs_echo_canceller::playback(pj_int16_t *play_frm, unsigned size) { /* Playing frame should be stored, as it will be used by echo_capture() * as reference frame, delay buffer is used for storing the playing frames * as in case there was clock drift between mic & speaker. * * Ticket #830: * Note that pjmedia_delay_buf_put() may modify the input frame and those * modified frames may not be smooth, i.e: if there were two or more * consecutive pjmedia_delay_buf_get() before next pjmedia_delay_buf_put(), * so we'll just feed the delay buffer with the copy of playing frame, * instead of the original playing frame. However this will cause the EC * uses slight 'different' frames (for reference) than actually played * by the speaker. */ if(samples_per_frame!=size) { PJ_LOG(1, (THIS_FILE, "WRONG SIZE ON PLAYBACK %d != %d",size,samples_per_frame)); return -1; } PPJ_WaitAndLock wl(*lock); pjmedia_copy_samples(frm_buf, play_frm, samples_per_frame); pjmedia_delay_buf_put(delay_buf, frm_buf); if (!lat_ready) { /* We've not built enough latency in the buffer, so put this frame * in the latency buffer list. */ struct frame *frm; if (pj_list_empty(&lat_free)) { lat_ready = PJ_TRUE; PJ_LOG(4, (THIS_FILE, "Latency bufferring complete")); return PJ_SUCCESS; } frm = lat_free.prev; pj_list_erase(frm); /* Move one frame from delay buffer to the latency buffer. */ pjmedia_delay_buf_get(delay_buf, frm_buf); pjmedia_copy_samples(frm->buf, frm_buf, samples_per_frame); pj_list_push_back(&lat_buf, frm); } return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjsip_publishc_set_headers( pjsip_publishc *pubc, const pjsip_hdr *hdr_list) { const pjsip_hdr *h; PJ_ASSERT_RETURN(pubc && hdr_list, PJ_EINVAL); pj_list_init(&pubc->usr_hdr); h = hdr_list->next; while (h != hdr_list) { pj_list_push_back(&pubc->usr_hdr, pjsip_hdr_clone(pubc->pool, h)); h = h->next; } return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjsip_ua_unregister_dlg( pjsip_user_agent *ua, pjsip_dialog *dlg ) { struct dlg_set *dlg_set; pjsip_dialog *d; /* Sanity-check arguments. */ PJ_ASSERT_RETURN(ua && dlg, PJ_EINVAL); /* Check that dialog has been registered. */ PJ_ASSERT_RETURN(dlg->dlg_set, PJ_EINVALIDOP); /* Lock user agent. */ pj_mutex_lock(mod_ua.mutex); /* Find this dialog from the dialog set. */ dlg_set = (struct dlg_set*) dlg->dlg_set; d = dlg_set->dlg_list.next; while (d != (pjsip_dialog*)&dlg_set->dlg_list && d != dlg) { d = d->next; } if (d != dlg) { pj_assert(!"Dialog is not registered!"); pj_mutex_unlock(mod_ua.mutex); return PJ_EINVALIDOP; } /* Remove this dialog from the list. */ pj_list_erase(dlg); /* If dialog list is empty, remove the dialog set from the hash table. */ if (pj_list_empty(&dlg_set->dlg_list)) { pj_hash_set_lower(NULL, mod_ua.dlg_table, dlg->local.info->tag.ptr, (unsigned)dlg->local.info->tag.slen, dlg->local.tag_hval, NULL); /* Return dlg_set to free nodes. */ pj_list_push_back(&mod_ua.free_dlgset_nodes, dlg_set); } /* Unlock user agent. */ pj_mutex_unlock(mod_ua.mutex); /* Done. */ return PJ_SUCCESS; }
/* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. */ static void decrement_counter(pj_ioqueue_key_t *key) { if (pj_atomic_dec_and_get(key->ref_count) == 0) { pj_lock_acquire(key->ioqueue->lock); pj_assert(key->closing == 1); pj_gettickcount(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); pj_lock_release(key->ioqueue->lock); } }
PJ_DEF(pj_status_t) pjsip_regc_set_route_set( pjsip_regc *regc, const pjsip_route_hdr *route_set) { const pjsip_route_hdr *chdr; PJ_ASSERT_RETURN(regc && route_set, PJ_EINVAL); pj_list_init(®c->route_set); chdr = route_set->next; while (chdr != route_set) { pj_list_push_back(®c->route_set, pjsip_hdr_clone(regc->pool, chdr)); chdr = chdr->next; } return PJ_SUCCESS; }
PJ_DECL(pj_status_t) csipsimple_msg_data_add_string_hdr(pj_pool_t* pool, pjsua_msg_data* msg_data, pj_str_t* hdr_name, pj_str_t* hdr_value){ // Sanity check PJ_ASSERT_RETURN(msg_data != NULL && hdr_name != NULL && hdr_value != NULL, PJ_EINVAL); if(hdr_name->slen <= 2 || hdr_value->slen <= 0){ return PJ_EINVAL; } // Ensure it's a X- prefixed header. This is to avoid crappy usage/override of specified headers // That should be implemented properly elsewhere. if(hdr_name->ptr[0] != 'X' || hdr_name->ptr[1] != '-'){ return PJ_EINVAL; } pjsip_generic_string_hdr* hdr = pjsip_generic_string_hdr_create(pool, hdr_name, hdr_value); // Push it to msg data pj_list_push_back(&msg_data->hdr_list, hdr); }
pjsip_multipart_clone_part(pj_pool_t *pool, const pjsip_multipart_part *src) { pjsip_multipart_part *dst; const pjsip_hdr *hdr; dst = pjsip_multipart_create_part(pool); hdr = src->hdr.next; while (hdr != &src->hdr) { pj_list_push_back(&dst->hdr, pjsip_hdr_clone(pool, hdr)); hdr = hdr->next; } dst->body = pjsip_msg_body_clone(pool, src->body); return dst; }
PJ_DEF(pj_status_t) pjsip_regc_add_headers( pjsip_regc *regc, const pjsip_hdr *hdr_list) { const pjsip_hdr *hdr; PJ_ASSERT_RETURN(regc && hdr_list, PJ_EINVAL); //This is "add" operation, so don't remove headers. //pj_list_init(®c->hdr_list); hdr = hdr_list->next; while (hdr != hdr_list) { pj_list_push_back(®c->hdr_list, pjsip_hdr_clone(regc->pool, hdr)); hdr = hdr->next; } return PJ_SUCCESS; }
/* * Register a codec factory. */ PJ_DEF(pj_status_t) pjmedia_codec_mgr_register_factory( pjmedia_codec_mgr *mgr, pjmedia_codec_factory *factory) { pjmedia_codec_info info[PJMEDIA_CODEC_MGR_MAX_CODECS]; unsigned i, count; pj_status_t status; PJ_ASSERT_RETURN(mgr && factory, PJ_EINVAL); /* Enum codecs */ count = PJ_ARRAY_SIZE(info); status = factory->op->enum_info(factory, &count, info); if (status != PJ_SUCCESS) return status; /* Check codec count */ if (count + mgr->codec_cnt > PJ_ARRAY_SIZE(mgr->codec_desc)) return PJ_ETOOMANY; /* Save the codecs */ for (i=0; i<count; ++i) { pj_memcpy( &mgr->codec_desc[mgr->codec_cnt+i], &info[i], sizeof(pjmedia_codec_info)); mgr->codec_desc[mgr->codec_cnt+i].prio = PJMEDIA_CODEC_PRIO_NORMAL; mgr->codec_desc[mgr->codec_cnt+i].factory = factory; pjmedia_codec_info_to_id( &info[i], mgr->codec_desc[mgr->codec_cnt+i].id, sizeof(pjmedia_codec_id)); } /* Update count */ mgr->codec_cnt += count; /* Re-sort codec based on priorities */ sort_codecs(mgr); /* Add factory to the list */ pj_list_push_back(&mgr->factory_list, factory); return PJ_SUCCESS; }
PJ_DEF(pj_status_t) pjmedia_endpt_atexit( pjmedia_endpt *endpt, pjmedia_endpt_exit_callback func) { exit_cb *new_cb; PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL); if (endpt->quit_flag) return PJ_EINVALIDOP; new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb); new_cb->func = func; pj_enter_critical_section(); pj_list_push_back(&endpt->exit_cb_list, new_cb); pj_leave_critical_section(); return PJ_SUCCESS; }
/* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettimeofday(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); pj_list_push_back(&ioqueue->free_list, h); } h = next; } }
/* Decrement the key's reference counter, and when the counter reach zero, * destroy the key. * * Note: MUST NOT CALL THIS FUNCTION WHILE HOLDING ioqueue's LOCK. */ static void decrement_counter(pj_ioqueue_key_t *key) { pj_lock_acquire(key->ioqueue->lock); pj_mutex_lock(key->ioqueue->ref_cnt_mutex); --key->ref_count; if (key->ref_count == 0) { pj_assert(key->closing == 1); pj_gettimeofday(&key->free_time); key->free_time.msec += PJ_IOQUEUE_KEY_FREE_DELAY; pj_time_val_normalize(&key->free_time); pj_list_erase(key); pj_list_push_back(&key->ioqueue->closing_list, key); } pj_mutex_unlock(key->ioqueue->ref_cnt_mutex); pj_lock_release(key->ioqueue->lock); }
PJ_DEF(pj_status_t) pjmedia_event_subscribe( pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) { esub *sub; PJ_ASSERT_RETURN(cb, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); /* Check whether callback function with the same user data is already * subscribed to the publisher. This is to prevent the callback function * receiving the same event from the same publisher more than once. */ sub = mgr->esub_list.next; while (sub != &mgr->esub_list) { esub *next = sub->next; if (sub->cb == cb && sub->user_data == user_data && sub->epub == epub) { pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; } sub = next; } if (mgr->free_esub_list.next != &mgr->free_esub_list) { sub = mgr->free_esub_list.next; pj_list_erase(sub); } else sub = PJ_POOL_ZALLOC_T(mgr->pool, esub); sub->cb = cb; sub->user_data = user_data; sub->epub = epub; pj_list_push_back(&mgr->esub_list, sub); pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; }
pj_status_t BlabbleCall::MakeCall(const std::string& dest, const std::string& identity) { BlabbleAccountPtr p = parent_.lock(); if (!p) return false; if (call_id_ != INVALID_CALL) return false; pj_status_t status; pj_str_t desturi; desturi.ptr = const_cast<char*>(dest.c_str()); desturi.slen = dest.length(); if (!identity.empty()) { pjsua_msg_data msgData; pjsua_msg_data_init(&msgData); pjsip_generic_string_hdr cidHdr; pj_str_t name = pj_str(const_cast<char*>("P-Asserted-Identity")); pj_str_t value = pj_str(const_cast<char*>(identity.c_str())); pjsip_generic_string_hdr_init2(&cidHdr, &name, &value); pj_list_push_back(&msgData.hdr_list, &cidHdr); status = pjsua_call_make_call(acct_id_, &desturi, 0, &id_, &msgData, (pjsua_call_id*)&call_id_); } else { status = pjsua_call_make_call(acct_id_, &desturi, 0, &id_, NULL, (pjsua_call_id*)&call_id_); } if (status == PJ_SUCCESS) { destination_ = dest; StartOutRinging(); } return status; }
/* * Enable multipart in msg_data and add a dummy body into the * multipart bodies. */ void add_multipart(pjsua_msg_data *msg_data) { static pjsip_multipart_part *alt_part; if (!alt_part) { pj_str_t type, subtype, content; alt_part = pjsip_multipart_create_part(app_config.pool); type = pj_str("text"); subtype = pj_str("plain"); content = pj_str("Sample text body of a multipart bodies"); alt_part->body = pjsip_msg_body_create(app_config.pool, &type, &subtype, &content); } msg_data->multipart_ctype.type = pj_str("multipart"); msg_data->multipart_ctype.subtype = pj_str("mixed"); pj_list_push_back(&msg_data->multipart_parts, alt_part); }
/* * Add a part into multipart bodies. */ PJ_DEF(pj_status_t) pjsip_multipart_add_part( pj_pool_t *pool, pjsip_msg_body *mp, pjsip_multipart_part *part) { struct multipart_data *m_data; /* All params must be specified */ PJ_ASSERT_RETURN(pool && mp && part, PJ_EINVAL); /* mp must really point to an actual multipart msg body */ PJ_ASSERT_RETURN(mp->print_body==&multipart_print_body, PJ_EINVAL); /* The multipart part must contain a valid message body */ PJ_ASSERT_RETURN(part->body && part->body->print_body, PJ_EINVAL); m_data = (struct multipart_data*)mp->data; pj_list_push_back(&m_data->part_head, part); PJ_UNUSED_ARG(pool); return PJ_SUCCESS; }
pjmedia_event_unsubscribe(pjmedia_event_mgr *mgr, pjmedia_event_cb *cb, void *user_data, void *epub) { esub *sub; PJ_ASSERT_RETURN(cb, PJ_EINVAL); if (!mgr) mgr = pjmedia_event_mgr_instance(); PJ_ASSERT_RETURN(mgr, PJ_EINVAL); pj_mutex_lock(mgr->mutex); sub = mgr->esub_list.next; while (sub != &mgr->esub_list) { esub *next = sub->next; if (sub->cb == cb && (sub->user_data == user_data || !user_data) && (sub->epub == epub || !epub)) { /* If the worker thread or pjmedia_event_publish() API is * in the process of distributing events, make sure that * its pointer to the next subscriber stays valid. */ if (mgr->th_next_sub == sub) mgr->th_next_sub = sub->next; if (mgr->pub_next_sub == sub) mgr->pub_next_sub = sub->next; pj_list_erase(sub); pj_list_push_back(&mgr->free_esub_list, sub); if (user_data && epub) break; } sub = next; } pj_mutex_unlock(mgr->mutex); return PJ_SUCCESS; }
/* * Let the Echo Canceller knows that a frame has been captured from * the microphone. */ PJ_DEF(pj_status_t) pjmedia_echo_capture( pjmedia_echo_state *echo, pj_int16_t *rec_frm, unsigned options ) { struct frame *oldest_frm; pj_status_t status, rc; /* If EC algo has capture handler, just pass the frame. */ if (echo->op->ec_capture) { return (*echo->op->ec_capture)(echo->state, rec_frm, options); } if (!echo->lat_ready) { /* Prefetching to fill in the desired latency */ PJ_LOG(5,(echo->obj_name, "Prefetching..")); return PJ_SUCCESS; } /* Retrieve oldest frame from the latency buffer */ oldest_frm = echo->lat_buf.next; pj_list_erase(oldest_frm); /* Cancel echo using this reference frame */ status = pjmedia_echo_cancel(echo, rec_frm, oldest_frm->buf, options, NULL); /* Move one frame from delay buffer to the latency buffer. */ rc = pjmedia_delay_buf_get(echo->delay_buf, oldest_frm->buf); if (rc != PJ_SUCCESS) { /* Ooops.. no frame! */ PJ_LOG(5,(echo->obj_name, "No frame from delay buffer. This will upset EC later")); pjmedia_zero_samples(oldest_frm->buf, echo->samples_per_frame); } pj_list_push_back(&echo->lat_buf, oldest_frm); return status; }
/* * Parse Min-SE header. */ static pjsip_hdr *parse_hdr_min_se(pjsip_parse_ctx *ctx) { pjsip_min_se_hdr *hdr = pjsip_min_se_hdr_create(ctx->pool); const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_str_t token; pj_scan_get(ctx->scanner, &pc->pjsip_DIGIT_SPEC, &token); hdr->min_se = pj_strtoul(&token); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; }
/* Scan closing keys to be put to free list again */ static void scan_closing_keys(pj_ioqueue_t *ioqueue) { pj_time_val now; pj_ioqueue_key_t *h; pj_gettickcount(&now); h = ioqueue->closing_list.next; while (h != &ioqueue->closing_list) { pj_ioqueue_key_t *next = h->next; pj_assert(h->closing != 0); if (PJ_TIME_VAL_GTE(now, h->free_time)) { pj_list_erase(h); // Don't set grp_lock to NULL otherwise the other thread // will crash. Just leave it as dangling pointer, but this // should be safe //h->grp_lock = NULL; pj_list_push_back(&ioqueue->free_list, h); } h = next; } }
/* * Parse Subscription-State header. */ static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) { pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(ctx->pool); const pj_str_t reason = { "reason", 6 }, expires = { "expires", 7 }, retry_after = { "retry-after", 11 }; const pjsip_parser_const_t *pc = pjsip_parser_const(); pj_scan_get(ctx->scanner, &pc->pjsip_TOKEN_SPEC, &hdr->sub_state); while (*ctx->scanner->curptr == ';') { pj_str_t pname, pvalue; pj_scan_get_char(ctx->scanner); pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); if (pj_stricmp(&pname, &reason) == 0) { hdr->reason_param = pvalue; } else if (pj_stricmp(&pname, &expires) == 0) { hdr->expires_param = pj_strtoul(&pvalue); } else if (pj_stricmp(&pname, &retry_after) == 0) { hdr->retry_after = pj_strtoul(&pvalue); } else { pjsip_param *param = PJ_POOL_ALLOC_T(ctx->pool, pjsip_param); param->name = pname; param->value = pvalue; pj_list_push_back(&hdr->other_param, param); } } pjsip_parse_end_hdr_imp( ctx->scanner ); return (pjsip_hdr*)hdr; }
static pj_status_t set_contact( pjsip_regc *regc, int contact_cnt, const pj_str_t contact[] ) { const pj_str_t CONTACT = { "Contact", 7 }; pjsip_contact_hdr *h; int i; /* Save existing contact list to removed_contact_hdr_list and * clear contact_hdr_list. */ pj_list_merge_last(®c->removed_contact_hdr_list, ®c->contact_hdr_list); /* Set the expiration of Contacts in to removed_contact_hdr_list * zero. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { h->expires = 0; h = h->next; } /* Process new contacts */ for (i=0; i<contact_cnt; ++i) { pjsip_contact_hdr *hdr; pj_str_t tmp; pj_strdup_with_null(regc->pool, &tmp, &contact[i]); hdr = (pjsip_contact_hdr*) pjsip_parse_hdr(regc->pool, &CONTACT, tmp.ptr, tmp.slen, NULL); if (hdr == NULL) { PJ_LOG(4,(THIS_FILE, "Invalid Contact: \"%.*s\"", (int)tmp.slen, tmp.ptr)); return PJSIP_EINVALIDURI; } /* Find the new contact in old contact list. If found, remove * the old header from the old header list. */ h = regc->removed_contact_hdr_list.next; while (h != ®c->removed_contact_hdr_list) { int rc; rc = pjsip_uri_cmp(PJSIP_URI_IN_CONTACT_HDR, h->uri, hdr->uri); if (rc == 0) { /* Match */ pj_list_erase(h); break; } h = h->next; } /* If add_xuid_param option is enabled and Contact URI is sip/sips, * add xuid parameter to assist matching the Contact URI in the * REGISTER response later. */ if (regc->add_xuid_param && (PJSIP_URI_SCHEME_IS_SIP(hdr->uri) || PJSIP_URI_SCHEME_IS_SIPS(hdr->uri))) { pjsip_param *xuid_param; pjsip_sip_uri *sip_uri; xuid_param = PJ_POOL_ZALLOC_T(regc->pool, pjsip_param); xuid_param->name = XUID_PARAM_NAME; pj_create_unique_string(regc->pool, &xuid_param->value); sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(hdr->uri); pj_list_push_back(&sip_uri->other_param, xuid_param); } pj_list_push_back(®c->contact_hdr_list, hdr); } return PJ_SUCCESS; }
/* * This callback is called by transport manager to send SIP message */ static pj_status_t tcp_send_msg(pjsip_transport *transport, pjsip_tx_data *tdata, const pj_sockaddr_t *rem_addr, int addr_len, void *token, pjsip_transport_callback callback) { struct tcp_transport *tcp = (struct tcp_transport*)transport; pj_ssize_t size; pj_bool_t delayed = PJ_FALSE; pj_status_t status = PJ_SUCCESS; /* Sanity check */ PJ_ASSERT_RETURN(transport && tdata, PJ_EINVAL); /* Check that there's no pending operation associated with the tdata */ PJ_ASSERT_RETURN(tdata->op_key.tdata == NULL, PJSIP_EPENDINGTX); /* Check the address is supported */ PJ_ASSERT_RETURN(rem_addr && (addr_len==sizeof(pj_sockaddr_in) || addr_len==sizeof(pj_sockaddr_in6)), PJ_EINVAL); /* Init op key. */ tdata->op_key.tdata = tdata; tdata->op_key.token = token; tdata->op_key.callback = callback; /* If asynchronous connect() has not completed yet, just put the * transmit data in the pending transmission list since we can not * use the socket yet. */ if (tcp->has_pending_connect) { /* * Looks like connect() is still in progress. Check again (this time * with holding the lock) to be sure. */ pj_lock_acquire(tcp->base.lock); if (tcp->has_pending_connect) { struct delayed_tdata *delayed_tdata; /* * connect() is still in progress. Put the transmit data to * the delayed list. * Starting from #1583 (https://trac.pjsip.org/repos/ticket/1583), * we also add timeout value for the transmit data. When the * connect() is completed, the timeout value will be checked to * determine whether the transmit data needs to be sent. */ delayed_tdata = PJ_POOL_ZALLOC_T(tdata->pool, struct delayed_tdata); delayed_tdata->tdata_op_key = &tdata->op_key; if (tdata->msg && tdata->msg->type == PJSIP_REQUEST_MSG) { pj_gettickcount(&delayed_tdata->timeout); delayed_tdata->timeout.msec += pjsip_cfg()->tsx.td; pj_time_val_normalize(&delayed_tdata->timeout); } pj_list_push_back(&tcp->delayed_list, delayed_tdata); status = PJ_EPENDING; /* Prevent pj_ioqueue_send() to be called below */ delayed = PJ_TRUE; } pj_lock_release(tcp->base.lock); }