⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 nua_stack.c

📁 Sofia SIP is an open-source SIP User-Agent library, compliant with the IETF RFC3261 specification.
💻 C
📖 第 1 页 / 共 5 页
字号:
}/* ---------------------------------------------------------------------- *//**@internal * * @class nua_client_request * * Each handle has a queue of client-side requests; if a request is pending, * a new request from API is added to the queue. After the request is * complete, it is removed from the queue and destroyed by the default. The * exception is the client requests bound to a dialog usage: they are saved * and re-used when the dialog usage is refreshed (and sometimes when the * usage is terminated). *  * The client request is subclassed and its behaviour modified using virtual * function table in #nua_client_methods_t. *  * The first three methods (crm_template(), crm_init(), crm_send()) are * called when the request is sent first time. *  * The crm_template() is called if a template request message is needed (for * example, in case of unregister, unsubscribe and unpublish, the template * message is taken from the request establishing the usage). *  * The crm_init() is called when the template message and dialog leg has * been created and populated by the tags procided by the application. Its * parameters msg and sip are pointer to the template request message that * is saved in the nua_client_request::cr_msg field. * * The crm_send() is called with a copy of the template message that has * been populated with all the fields included in the request, including * @CSeq and @MaxForwards. The crm_send() function, such as * nua_publish_client_request(), usually calls nua_base_client_trequest() that * then creates the nta-level transaction. * * The response to the request is processed by crm_check_restart(), which * modifies and restarts the request when needed (e.g., when negotiating * expiration time). After the request has been suitably modified, e.g., the * expiration time has been increased, the restart function calls * nua_client_restart(), which restarts the request and relays the * intermediate response to the application with nua_client_restart() and * crm_report(). * * The final responses are processed by crm_recv() and and preliminary ones * by crm_preliminary(). Both functions call nua_base_client_response() after * method-specific processing. * * The nua_base_client_response() relays the response to the application with * nua_client_restart() and crm_report(). * * @par Terminating Dialog Usages and Dialogs * * The response can be marked as terminating with nua_client_terminating().  * When a terminating request completes the dialog usage is removed and the * dialog is destroyed (unless there is an another active usage). */static int nua_client_request_try(nua_client_request_t *cr);static int nua_client_request_sendmsg(nua_client_request_t *cr,  				      msg_t *msg, sip_t *sip);static void nua_client_restart_after(su_root_magic_t *magic,				     su_timer_t *timer,				     nua_client_request_t *cr);/**Create a client request. * * @retval 0 if request is pending * @retval > 0 if error event has been sent * @retval < 0 upon an error */int nua_client_create(nua_handle_t *nh, 		      int event,		      nua_client_methods_t const *methods,		      tagi_t const * const tags){  su_home_t *home = nh->nh_home;  nua_client_request_t *cr;  sip_method_t method;  char const *name;  method = methods->crm_method, name = methods->crm_method_name;  if (!name) {    tagi_t const *t = tl_find_last(tags, nutag_method);    if (t)      name = (char const *)t->t_value;  }  cr = su_zalloc(home, sizeof *cr + methods->crm_extra);  if (!cr) {    return nua_stack_event(nh->nh_nua, nh, 			   NULL,			   event,			   NUA_ERROR_AT(__FILE__, __LINE__),			   NULL);  }  cr->cr_methods = methods;  cr->cr_event = event;  cr->cr_method = method;  cr->cr_method_name = name;  cr->cr_contactize = methods->crm_flags.target_refresh;  cr->cr_dialog = methods->crm_flags.create_dialog;  cr->cr_auto = 1;  if (su_msg_is_non_null(nh->nh_nua->nua_signal)) {    nua_event_data_t *e = su_msg_data(nh->nh_nua->nua_signal)->ee_data;    if (tags == e->e_tags && event == e->e_event) {      cr->cr_auto = 0;      if (tags) {	nua_move_signal(cr->cr_signal, nh->nh_nua->nua_signal);	if (cr->cr_signal) {	  /* Steal reference from signal */	  cr->cr_owner = e->e_nh, e->e_nh = NULL;	  cr->cr_tags = tags;	}      }    }  }  if (cr->cr_owner == NULL)    cr->cr_owner = nua_handle_ref(nh);  if (tags && cr->cr_tags == NULL)    cr->cr_tags = tl_tlist(nh->nh_home, TAG_NEXT(tags));  if (nua_client_request_queue(cr))    return 0;  return nua_client_init_request(cr);}int nua_client_tcreate(nua_handle_t *nh, 		       int event,		       nua_client_methods_t const *methods,		       tag_type_t tag, tag_value_t value, ...){  int retval;  ta_list ta;  ta_start(ta, tag, value);  retval = nua_client_create(nh, event, methods, ta_args(ta));  ta_end(ta);  return retval;}int nua_client_request_queue(nua_client_request_t *cr){  int queued = 0;  nua_client_request_t **queue = &cr->cr_owner->nh_ds->ds_cr;  assert(cr->cr_prev == NULL && cr->cr_next == NULL);  cr->cr_status = 0;  if (cr->cr_method != sip_method_invite &&      cr->cr_method != sip_method_cancel) {    while (*queue) {      if ((*queue)->cr_method == sip_method_invite ||	  (*queue)->cr_method == sip_method_cancel)	break;      queue = &(*queue)->cr_next;      queued = 1;    }  }  else {    while (*queue) {      queue = &(*queue)->cr_next;      if (cr->cr_method == sip_method_invite)	queued = 1;    }  }  if ((cr->cr_next = *queue))    cr->cr_next->cr_prev = &cr->cr_next;  cr->cr_prev = queue, *queue = cr;  return queued;}nua_client_request_t *nua_client_request_remove(nua_client_request_t *cr){  if (cr->cr_prev)    if ((*cr->cr_prev = cr->cr_next))      cr->cr_next->cr_prev = cr->cr_prev;  cr->cr_prev = NULL, cr->cr_next = NULL;  return cr;}void nua_client_request_complete(nua_client_request_t *cr){  nua_client_request_remove(cr);  if (cr && cr->cr_methods->crm_complete)    cr->cr_methods->crm_complete(cr);}void nua_client_request_destroy(nua_client_request_t *cr){  nua_handle_t *nh;    if (cr == NULL)    return;  nua_client_request_complete(cr);  nh = cr->cr_owner;  nua_destroy_signal(cr->cr_signal);  nua_client_bind(cr, NULL);    if (cr->cr_msg)    msg_destroy(cr->cr_msg);  cr->cr_msg = NULL, cr->cr_sip = NULL;  if (cr->cr_orq)    nta_outgoing_destroy(cr->cr_orq);  cr->cr_orq = NULL;  if (cr->cr_timer)    su_timer_destroy(cr->cr_timer), cr->cr_timer = NULL;  if (cr->cr_target)    su_free(nh->nh_home, cr->cr_target);  su_free(nh->nh_home, cr);  nua_handle_unref(nh);}/** Bind client request to a dialog usage */ int nua_client_bind(nua_client_request_t *cr, nua_dialog_usage_t *du){  assert(cr);  if (cr == NULL)    return -1;  if (du == NULL) {    if (cr->cr_usage && cr->cr_usage->du_cr == cr)      cr->cr_usage->du_cr = NULL;    cr->cr_usage = NULL;    return 0;  }  if (du->du_cr && cr != du->du_cr) {    /* This should never happen (but it does):       assert(!nua_client_is_queued(du->du_cr));     */    if (nua_client_is_queued(du->du_cr))      return -1;    if (nua_client_is_reporting(du->du_cr)) {      du->du_cr->cr_usage = NULL;      du->du_cr = NULL;    }    else      nua_client_request_destroy(du->du_cr);  }  du->du_cr = cr, cr->cr_usage = du;  return 0;}/**Initialize client request for sending. * * This function is called only first time the request is sent. * * @retval 0 if request is pending * @retval >=1 if error event has been sent */int nua_client_init_request(nua_client_request_t *cr){  nua_handle_t *nh = cr->cr_owner;  nua_t *nua = nh->nh_nua;  nua_dialog_state_t *ds = nh->nh_ds;  msg_t *msg = NULL;  sip_t *sip;  url_string_t const *url = NULL;  tagi_t const *t;  int has_contact = 0;  int error = 0;    if (!cr->cr_method_name)    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), NULL);  if (cr->cr_msg)    return nua_client_request_try(cr);  cr->cr_answer_recv = 0, cr->cr_offer_sent = 0;  cr->cr_offer_recv = 0, cr->cr_answer_sent = 0;  cr->cr_terminated = 0, cr->cr_graceful = 0;  nua_stack_init_handle(nua, nh, cr->cr_tags);  if (cr->cr_method == sip_method_cancel) {    if (cr->cr_methods->crm_init) {      error = cr->cr_methods->crm_init(cr, NULL, NULL, cr->cr_tags);      if (error)	return error;    }    if (cr->cr_methods->crm_send)      return cr->cr_methods->crm_send(cr, NULL, NULL, cr->cr_tags);    else      return nua_base_client_request(cr, NULL, NULL, cr->cr_tags);  }  if (!cr->cr_methods->crm_template ||      cr->cr_methods->crm_template(cr, &msg, cr->cr_tags) == 0)    msg = nua_client_request_template(cr);  sip = sip_object(msg);  if (!sip)    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);  if (nh->nh_tags) {    for (t = nh->nh_tags; t; t = t_next(t)) {      if (t->t_tag == siptag_contact ||	  t->t_tag == siptag_contact_str)	has_contact = 1;      else if (t->t_tag == nutag_url)	url = (url_string_t const *)t->t_value;    }  }  /**@par Populating SIP Request Message with Tagged Arguments   *   * The tagged arguments can be used to pass values for any SIP headers   * to the stack. When the INVITE message (or any other SIP message) is   * created, the tagged values saved with nua_handle() are used first,   * next the tagged values given with the operation (nua_invite()) are   * added.   *   * When multiple tags for the same header are specified, the behaviour   * depends on the header type. If only a single header field can be   * included in a SIP message, the latest non-NULL value is used, e.g.,   * @Subject. However, if the SIP header can consist of multiple lines or   * header fields separated by comma, e.g., @Accept, all the tagged   * values are concatenated.   *   * However, if a tag value is #SIP_NONE (-1 casted as a void pointer),   * the values from previous tags are ignored.   */  for (t = cr->cr_tags; t; t = t_next(t)) {    if (t->t_tag == siptag_contact ||	t->t_tag == siptag_contact_str)      has_contact = 1;    else if (t->t_tag == nutag_url)      url = (url_string_t const *)t->t_value;    else if (t->t_tag == nutag_dialog) {      cr->cr_dialog = t->t_value > 1;      cr->cr_contactize = t->t_value >= 1;    }    else if (t->t_tag == nutag_auth && t->t_value) {      /* XXX ignoring errors */      if (nh->nh_auth)	auc_credentials(&nh->nh_auth, nh->nh_home, (char *)t->t_value);    }  }  if (cr->cr_method == sip_method_register && url == NULL)    url = (url_string_t const *)NH_PGET(nh, registrar);    if ((t = cr->cr_tags)) {    if (sip_add_tagis(msg, sip, &t) < 0)      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);  }  /**   * Now, the target URI for the request needs to be determined.   *   * For initial requests, values from tags are used. If NUTAG_URL() is   * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given,   * it is used as target URI. If neither is given, the complete request   * line already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR()   * is used. At this point, the target URI is stored in the request line,   * together with method name and protocol version ("SIP/2.0"). The   * initial dialog information is also created: @CallID, @CSeq headers   * are generated, if they do not exist, and a tag is added to the @From   * header.   */  if (!ds->ds_leg) {    if (ds->ds_remote_tag && ds->ds_remote_tag[0] && 	sip_to_tag(nh->nh_home, sip->sip_to, ds->ds_remote_tag) < 0)      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);    if (sip->sip_from == NULL && 	sip_add_dup(msg, sip, (sip_header_t *)nua->nua_from) < 0)      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);    if (sip->sip_to == NULL && cr->cr_method == sip_method_register &&      sip_add_dup_as(msg, sip, sip_to_class,		     (sip_header_t *)sip->sip_from) < 0) {      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);    }    if (cr->cr_dialog) {      ds->ds_leg = nta_leg_tcreate(nua->nua_nta,				   nua_stack_process_request, nh,				   SIPTAG_CALL_ID(sip->sip_call_id),				   SIPTAG_FROM(sip->sip_from),				   SIPTAG_TO(sip->sip_to),				   SIPTAG_CSEQ(sip->sip_cseq),				   TAG_END());      if (!ds->ds_leg)	return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);      if (!sip->sip_from->a_tag &&	  sip_from_tag(msg_home(msg), sip->sip_from,		       nta_leg_tag(ds->ds_leg, NULL)) < 0)	return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);    }  }  else {    if (ds->ds_route)      url = NULL;  }  if (url && nua_client_set_target(cr, (url_t *)url) < 0)    return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);  cr->cr_has_contact = has_contact;  if (cr->cr_methods->crm_init) {    error = cr->cr_methods->crm_init(cr, msg, sip, cr->cr_tags);    if (error < -1)      msg = NULL;    if (error < 0)      return nua_client_return(cr, NUA_ERROR_AT(__FILE__, __LINE__), msg);    if (error != 0)      return error;  }  cr->cr_msg = msg;  cr->cr_sip = sip;  return nua_client_request_try(cr);}msg_t *nua_client_request_template(nua_client_request_t *cr){  nua_handle_t *nh = cr->cr_owner;  nua_t *nua = nh->nh_nua;  nua_dialog_state_t *ds = nh->nh_ds;  msg_t *msg = nta_msg_create(nua->nua_nta, 0);  sip_t *sip = sip_object(msg);  if (!sip)    return NULL;  if (nh->nh_tags) {    tagi_t const *t = nh->nh_tags;    /* Use the From header from the dialog.        From is always first tag in the handle */    if (ds->ds_leg && t->t_tag == siptag_from)      t++;    /* When the INVITE message (or any other SIP message) is     * created, the tagged values saved with nua_handle() are used first. */    sip_add_tagis(msg, sip, &t);  }  return msg;}/** Restart the request message. * * A restarted request has not been completed successfully. * * @retval 0 if request is pending * @retval >=1 if error event has been sent 

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -