diff options
-rw-r--r-- | pjsip-apps/src/pjsua/pjsua_app.c | 87 | ||||
-rw-r--r-- | pjsip/include/pjsip-simple/evsub.h | 12 | ||||
-rw-r--r-- | pjsip/include/pjsip-ua/sip_inv.h | 15 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 11 | ||||
-rw-r--r-- | pjsip/src/pjsip-simple/evsub.c | 21 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 40 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 10 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_acc.c | 4 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 120 |
9 files changed, 303 insertions, 17 deletions
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c index e4fd5e85..999df2a5 100644 --- a/pjsip-apps/src/pjsua/pjsua_app.c +++ b/pjsip-apps/src/pjsua/pjsua_app.c @@ -137,7 +137,12 @@ static void usage(void) /* Set default config. */ static void default_config(struct app_config *cfg) { + char tmp[80]; + pjsua_config_default(&cfg->cfg); + pj_ansi_sprintf(tmp, "PJSUA v%s/%s", PJ_VERSION, PJ_OS_NAME); + pj_strdup2_with_null(app_config.pool, &cfg->cfg.user_agent, tmp); + pjsua_logging_config_default(&cfg->log_cfg); pjsua_media_config_default(&cfg->media_cfg); pjsua_transport_config_default(&cfg->udp_cfg); @@ -472,7 +477,7 @@ static pj_status_t parse_args(int argc, char *argv[], case OPT_NEXT_ACCOUNT: /* Add more account. */ cfg->acc_cnt++; - cur_acc = &cfg->acc_cfg[cfg->acc_cnt - 1]; + cur_acc = &cfg->acc_cfg[cfg->acc_cnt]; break; case OPT_USERNAME: /* Default authentication user */ @@ -664,8 +669,8 @@ static pj_status_t parse_args(int argc, char *argv[], return PJ_EINVAL; } - if (cfg->acc_cfg[0].id.slen && cfg->acc_cnt==0) - cfg->acc_cnt = 1; + if (cfg->acc_cfg[cfg->acc_cnt].id.slen) + cfg->acc_cnt++; for (i=0; i<cfg->acc_cnt; ++i) { if (cfg->acc_cfg[i].cred_info[cfg->acc_cfg[i].cred_count].username.slen) @@ -1306,7 +1311,7 @@ static void keystroke_help(void) puts("| dq Dump curr. call quality | cl List ports | d Dump status |"); puts("| | cc Connect port | dd Dump detailed |"); puts("| | cd Disconnect port | dc Dump config |"); - puts("| | | f Save config |"); + puts("| S Send arbitrary REQUEST | | f Save config |"); puts("+------------------------------+--------------------------+-------------------+"); puts("| q QUIT |"); puts("+=============================================================================+"); @@ -1463,6 +1468,42 @@ static void conf_list(void) /* + * Send arbitrary request to remote host + */ +static void send_request(char *cstr_method, const pj_str_t *dst_uri) +{ + pj_str_t str_method; + pjsip_method method; + pjsip_tx_data *tdata; + pjsua_acc_info acc_info; + pjsip_endpoint *endpt; + pj_status_t status; + + endpt = pjsua_get_pjsip_endpt(); + + str_method = pj_str(cstr_method); + pjsip_method_init_np(&method, &str_method); + + pjsua_acc_get_info(current_acc, &acc_info); + + status = pjsip_endpt_create_request(endpt, &method, dst_uri, + &acc_info.acc_uri, dst_uri, + NULL, NULL, -1, NULL, &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create request", status); + return; + } + + status = pjsip_endpt_send_request(endpt, tdata, -1, NULL, NULL); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to send request", status); + pjsip_tx_data_dec_ref(tdata); + return; + } +} + + +/* * Main "user interface" loop. */ void console_app_main(const pj_str_t *uri_to_call) @@ -1893,6 +1934,44 @@ void console_app_main(const pj_str_t *uri_to_call) } break; + case 'S': + /* + * Send arbitrary request + */ + if (pjsua_acc_get_count() == 0) { + puts("Sorry, need at least one account configured"); + break; + } + + puts("Send arbitrary request to remote host"); + + /* Input METHOD */ + if (!simple_input("Request method:",text,sizeof(text))) + break; + + /* Input destination URI */ + uri = NULL; + ui_input_url("Destination URI", buf, sizeof(buf), &result); + if (result.nb_result != NO_NB) { + + if (result.nb_result == -1 || result.nb_result == 0) { + puts("Sorry you can't do that!"); + continue; + } else { + pjsua_buddy_info binfo; + pjsua_buddy_get_info(result.nb_result-1, &binfo); + uri = binfo.uri.ptr; + } + + } else if (result.uri_result) { + uri = result.uri_result; + } + + tmp = pj_str(uri); + + send_request(text, &tmp); + break; + case 's': case 'u': /* diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h index 4eb2ae1c..e352e798 100644 --- a/pjsip/include/pjsip-simple/evsub.h +++ b/pjsip/include/pjsip-simple/evsub.h @@ -252,6 +252,18 @@ PJ_DECL(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, unsigned accept_cnt, const pj_str_t accept[]); +/** + * Get the Allow-Events header. This header is built based on the packages + * that are registered to the evsub module. + * + * @param m Pointer to event subscription module instance, or + * NULL to use default instance (equal to + * #pjsip_evsub_instance()). + * + * @return The Allow-Events header. + */ +PJ_DECL(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m); + /** * Create client subscription session. diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h index e07ba79a..cb6dc251 100644 --- a/pjsip/include/pjsip-ua/sip_inv.h +++ b/pjsip/include/pjsip-ua/sip_inv.h @@ -615,6 +615,21 @@ PJ_DECL(pjsip_inv_session*) pjsip_tsx_get_inv_session(pjsip_transaction *tsx); PJ_DECL(const char *) pjsip_inv_state_name(pjsip_inv_state state); +/** + * This is a utility function to create SIP body for SDP content. + * + * @param pool Pool to allocate memory. + * @param sdp SDP session to be put in the SIP message body. + * @param p_body Pointer to receive SIP message body containing + * the SDP session. + * + * @return PJ_SUCCESS on success. + */ +PJ_DECL(pj_status_t) pjsip_create_sdp_body(pj_pool_t *pool, + pjmedia_sdp_session *sdp, + pjsip_msg_body **p_body); + + PJ_END_DECL /** diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 7ae37eed..5b8125a2 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -417,6 +417,11 @@ typedef struct pjsua_config */ pjsua_callback cb; + /** + * User agent string (default empty) + */ + pj_str_t user_agent; + } pjsua_config; @@ -468,6 +473,8 @@ PJ_INLINE(void) pjsua_config_dup(pj_pool_t *pool, for (i=0; i<src->cred_count; ++i) { pjsip_cred_dup(pool, &dst->cred_info[i], &src->cred_info[i]); } + + pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent); } @@ -2080,7 +2087,7 @@ struct pjsua_media_config * The media quality also sets speex codec quality/complexity to the * number. * - * Default: 3. + * Default: 5. */ unsigned quality; }; @@ -2099,7 +2106,7 @@ PJ_INLINE(void) pjsua_media_config_default(pjsua_media_config *cfg) cfg->max_media_ports = 32; cfg->has_ioqueue = PJ_TRUE; cfg->thread_cnt = 1; - cfg->quality = 3; + cfg->quality = 5; } diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c index 7b13d548..e5b4f526 100644 --- a/pjsip/src/pjsip-simple/evsub.c +++ b/pjsip/src/pjsip-simple/evsub.c @@ -420,6 +420,12 @@ PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, ++mod_evsub.allow_events_hdr->count; } + /* Add to endpoint's Accept header */ + pjsip_endpt_add_capability(mod_evsub.endpt, &mod_evsub.mod, + PJSIP_H_ACCEPT, NULL, + pkg->pkg_accept->count, + pkg->pkg_accept->values); + /* Done */ @@ -431,6 +437,21 @@ PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, } +/* + * Retrieve Allow-Events header + */ +PJ_DEF(const pjsip_hdr*) pjsip_evsub_get_allow_events_hdr(pjsip_module *m) +{ + struct mod_evsub *mod; + + if (m == NULL) + m = pjsip_evsub_instance(); + + mod = (struct mod_evsub*)m; + + return (pjsip_hdr*) mod->allow_events_hdr; +} + /* * Update expiration time. diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index d3176862..e35f0eea 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -112,11 +112,16 @@ struct tsx_inv_data static pj_status_t mod_inv_load(pjsip_endpoint *endpt) { pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}}; + pj_str_t accepted = { "application/sdp", 15 }; /* Register supported methods: INVITE, ACK, BYE, CANCEL */ pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL, PJ_ARRAY_SIZE(allowed), allowed); + /* Register "application/sdp" in Accept header */ + pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ACCEPT, NULL, + 1, &accepted); + return PJ_SUCCESS; } @@ -959,22 +964,43 @@ static int print_sdp(pjsip_msg_body *body, char *buf, pj_size_t len) return pjmedia_sdp_print(body->data, buf, len); } -static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, - const pjmedia_sdp_session *c_sdp) + +PJ_DEF(pj_status_t) pjsip_create_sdp_body( pj_pool_t *pool, + pjmedia_sdp_session *sdp, + pjsip_msg_body **p_body) { + const pj_str_t STR_APPLICATION = { "application", 11}; + const pj_str_t STR_SDP = { "sdp", 3 }; pjsip_msg_body *body; - body = pj_pool_zalloc(pool, sizeof(pjsip_msg_body)); - PJ_ASSERT_RETURN(body != NULL, NULL); + PJ_ASSERT_RETURN(body != NULL, PJ_ENOMEM); - body->content_type.type = pj_str("application"); - body->content_type.subtype = pj_str("sdp"); - body->data = pjmedia_sdp_session_clone(pool, c_sdp); + body->content_type.type = STR_APPLICATION; + body->content_type.subtype = STR_SDP; + body->data = sdp; body->len = 0; body->clone_data = &clone_sdp; body->print_body = &print_sdp; + *p_body = body; + + return PJ_SUCCESS; +} + +static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, + const pjmedia_sdp_session *c_sdp) +{ + pjsip_msg_body *body; + pj_status_t status; + + status = pjsip_create_sdp_body(pool, + pjmedia_sdp_session_clone(pool, c_sdp), + &body); + + if (status != PJ_SUCCESS) + return NULL; + return body; } diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index 9b59d258..c84cfd20 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -1344,8 +1344,9 @@ void pjsip_dlg_on_rx_request( pjsip_dialog *dlg, pjsip_rx_data *rdata ) pjsip_tx_data *tdata; const pj_str_t reason = { "No session found", 16}; - PJ_LOG(4,(tsx->obj_name, "Incoming request was unhandled by " - "dialog usages, sending 500 response")); + PJ_LOG(4,(tsx->obj_name, "%s was unhandled by " + "dialog usages, sending 500 response", + pjsip_rx_data_get_info(rdata))); status = pjsip_dlg_create_response(dlg, rdata, 500, &reason, &tdata); if (status == PJ_SUCCESS) { @@ -1457,10 +1458,13 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) break; } + /* Unhandled response does not necessarily mean error because + dialog usages may choose to process the transaction state instead. if (i==dlg->usage_cnt) { - PJ_LOG(4,(dlg->obj_name, "%s is unhandled by dialog usages", + PJ_LOG(4,(dlg->obj_name, "%s was not claimed by any dialog usages", pjsip_rx_data_get_info(rdata))); } + */ /* Unlock dialog and dec session, may destroy dialog. */ pjsip_dlg_dec_lock(dlg); diff --git a/pjsip/src/pjsua-lib/pjsua_acc.c b/pjsip/src/pjsua-lib/pjsua_acc.c index 61b4096a..1d5873be 100644 --- a/pjsip/src/pjsua-lib/pjsua_acc.c +++ b/pjsip/src/pjsua-lib/pjsua_acc.c @@ -538,8 +538,10 @@ PJ_DEF(pj_status_t) pjsua_acc_set_registration( pjsua_acc_id acc_id, status = pjsip_regc_unregister(pjsua_var.acc[acc_id].regc, &tdata); } - if (status == PJ_SUCCESS) + if (status == PJ_SUCCESS) { + pjsua_process_msg_data(tdata, NULL); status = pjsip_regc_send( pjsua_var.acc[acc_id].regc, tdata ); + } if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create/send REGISTER", diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 98480b9a..930543ea 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -129,6 +129,106 @@ static pjsip_module pjsua_msg_logger = /***************************************************************************** + * Another simple module to handle incoming OPTIONS request + */ + +/* Notification on incoming request */ +static pj_bool_t options_on_rx_request(pjsip_rx_data *rdata) +{ + pjsip_tx_data *tdata; + pjsip_response_addr res_addr; + pjmedia_sdp_session *sdp; + const pjsip_hdr *cap_hdr; + pj_status_t status; + + /* Only want to handle OPTIONS requests */ + if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, + &pjsip_options_method) != 0) + { + return PJ_FALSE; + } + + /* Create basic response. */ + status = pjsip_endpt_create_response(pjsua_var.endpt, rdata, 200, NULL, + &tdata); + if (status != PJ_SUCCESS) { + pjsua_perror(THIS_FILE, "Unable to create OPTIONS response", status); + return PJ_TRUE; + } + + /* Add Allow header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ALLOW, NULL); + if (cap_hdr) { + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Accept header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_ACCEPT, NULL); + if (cap_hdr) { + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Supported header */ + cap_hdr = pjsip_endpt_get_capability(pjsua_var.endpt, PJSIP_H_SUPPORTED, NULL); + if (cap_hdr) { + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add Allow-Events header from the evsub module */ + cap_hdr = pjsip_evsub_get_allow_events_hdr(NULL); + if (cap_hdr) { + pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool, cap_hdr)); + } + + /* Add User-Agent header */ + if (pjsua_var.ua_cfg.user_agent.slen) { + const pj_str_t USER_AGENT = { "User-Agent", 10}; + pjsip_hdr *h; + + h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool, + &USER_AGENT, + &pjsua_var.ua_cfg.user_agent); + pjsip_msg_add_hdr(tdata->msg, h); + } + + /* Add SDP body, using call0's RTP address */ + status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, tdata->pool, 1, + &pjsua_var.calls[0].skinfo, &sdp); + if (status == PJ_SUCCESS) { + pjsip_create_sdp_body(tdata->pool, sdp, &tdata->msg->body); + } + + /* Send response statelessly */ + pjsip_get_response_addr(tdata->pool, rdata, &res_addr); + status = pjsip_endpt_send_response(pjsua_var.endpt, &res_addr, tdata, NULL, NULL); + if (status != PJ_SUCCESS) + pjsip_tx_data_dec_ref(tdata); + + return PJ_TRUE; +} + + +/* The module instance. */ +static pjsip_module pjsua_options_handler = +{ + NULL, NULL, /* prev, next. */ + { "mod-pjsua-options", 17 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &options_on_rx_request, /* on_rx_request() */ + NULL, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + +}; + + +/***************************************************************************** * These two functions are the main callbacks registered to PJSIP stack * to receive SIP request and response messages that are outside any * dialogs and any transactions. @@ -234,6 +334,12 @@ PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) } } + /* Unregister OPTIONS handler if it's previously registered */ + if (pjsua_options_handler.id >= 0) { + pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_options_handler); + pjsua_options_handler.id = -1; + } + /* Unregister msg logging if it's previously registered */ if (pjsua_msg_logger.id >= 0) { pjsip_endpt_unregister_module(pjsua_var.endpt, &pjsua_msg_logger); @@ -244,6 +350,8 @@ PJ_DEF(pj_status_t) pjsua_reconfigure_logging(const pjsua_logging_config *cfg) if (pjsua_var.log_cfg.msg_logging) pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_msg_logger); + /* Register OPTIONS handler */ + pjsip_endpt_register_module(pjsua_var.endpt, &pjsua_options_handler); return PJ_SUCCESS; } @@ -1024,6 +1132,18 @@ void pjsua_process_msg_data(pjsip_tx_data *tdata, pj_bool_t allow_body; const pjsip_hdr *hdr; + /* Always add User-Agent */ + if (pjsua_var.ua_cfg.user_agent.slen && + tdata->msg->type == PJSIP_REQUEST_MSG) + { + const pj_str_t STR_USER_AGENT = { "User-Agent", 10 }; + pjsip_hdr *h; + h = (pjsip_hdr*)pjsip_generic_string_hdr_create(tdata->pool, + &STR_USER_AGENT, + &pjsua_var.ua_cfg.user_agent); + pjsip_msg_add_hdr(tdata->msg, h); + } + if (!msg_data) return; |