diff options
author | Benny Prijono <bennylp@teluu.com> | 2006-06-06 18:40:40 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2006-06-06 18:40:40 +0000 |
commit | 8db4ef281d67eee2ebeac22a31bc1961e96d78b2 (patch) | |
tree | b910ef526c864da15ab3d05840fc78ff8c6c7608 /pjsip | |
parent | 40d75a0cb404fc0bafa20934e992befd0eab673b (diff) |
Another huge chunks of modifications in PJSUA API, too many things to mention!
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@492 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua.h | 175 | ||||
-rw-r--r-- | pjsip/include/pjsua-lib/pjsua_console_app.h | 2 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_call.c | 98 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_console_app.c | 148 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_core.c | 187 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_im.c | 34 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_imp.h | 25 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_pres.c | 67 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_reg.c | 61 | ||||
-rw-r--r-- | pjsip/src/pjsua-lib/pjsua_settings.c | 151 |
10 files changed, 665 insertions, 283 deletions
diff --git a/pjsip/include/pjsua-lib/pjsua.h b/pjsip/include/pjsua-lib/pjsua.h index 622991f6..dba8560c 100644 --- a/pjsip/include/pjsua-lib/pjsua.h +++ b/pjsip/include/pjsua-lib/pjsua.h @@ -68,6 +68,13 @@ PJ_BEGIN_DECL #endif +typedef int pjsua_acc_id; +typedef int pjsua_buddy_id; +typedef int pjsua_player_id; +typedef int pjsua_recorder_id; +typedef int pjsua_conf_port_id; + + /** * Account configuration. */ @@ -135,6 +142,11 @@ struct pjsua_config */ unsigned start_rtp_port; + /** + * Enable incoming and outgoing message logging (default: 1). + */ + pj_bool_t msg_logging; + /** Maximum calls to support (default: 4) */ unsigned max_calls; @@ -223,9 +235,6 @@ struct pjsua_config /** Outbound proxy (default: none) */ pj_str_t outbound_proxy; - /** URI to call. */ - pj_str_t uri_to_call; - /** Number of SIP accounts */ unsigned acc_cnt; @@ -272,6 +281,12 @@ struct pjsua_callback void (*on_call_state)(int call_index, pjsip_event *e); /** + * Notify application on incoming call. + */ + void (*on_incoming_call)(pjsua_acc_id acc_id, int call_index, + pjsip_rx_data *rdata); + + /** * Notify application on call being transfered. * Application can decide to accept/reject transfer request * by setting the code (default is 200). When this callback @@ -287,13 +302,13 @@ struct pjsua_callback * Application may then query the account info to get the * registration details. */ - void (*on_reg_state)(int acc_index); + void (*on_reg_state)(pjsua_acc_id acc_id); /** * Notify application when the buddy state has changed. * Application may then query the buddy into to get the details. */ - void (*on_buddy_state)(int buddy_index); + void (*on_buddy_state)(pjsua_buddy_id buddy_id); /** * Notify application on incoming pager (i.e. MESSAGE request). @@ -329,12 +344,12 @@ struct pjsua_call_info pj_str_t remote_info; pjsip_inv_state state; pj_str_t state_text; + pjsip_status_code last_status; + pj_str_t last_status_text; pj_time_val connect_duration; pj_time_val total_duration; - pjsip_status_code cause; - pj_str_t cause_text; pj_bool_t has_media; - unsigned conf_slot; + pjsua_conf_port_id conf_slot; }; typedef struct pjsua_call_info pjsua_call_info; @@ -355,7 +370,7 @@ typedef enum pjsua_buddy_status pjsua_buddy_status; */ struct pjsua_buddy_info { - unsigned index; + pjsua_buddy_id index; pj_bool_t is_valid; pj_str_t name; pj_str_t display_name; @@ -365,7 +380,6 @@ struct pjsua_buddy_info pjsua_buddy_status status; pj_str_t status_text; pj_bool_t monitor; - int acc_index; }; typedef struct pjsua_buddy_info pjsua_buddy_info; @@ -376,7 +390,7 @@ typedef struct pjsua_buddy_info pjsua_buddy_info; */ struct pjsua_acc_info { - unsigned index; + pjsua_acc_id index; pj_str_t acc_id; pj_bool_t has_registration; int expires; @@ -389,9 +403,23 @@ struct pjsua_acc_info typedef struct pjsua_acc_info pjsua_acc_info; -typedef int pjsua_player_id; -typedef int pjsua_recorder_id; +/** + * Conference port info. + */ +struct pjsua_conf_port_info +{ + pjsua_conf_port_id slot_id; + pj_str_t name; + unsigned clock_rate; + unsigned channel_count; + unsigned samples_per_frame; + unsigned bits_per_sample; + unsigned listener_cnt; + pjsua_conf_port_id listeners[256]; +}; + +typedef struct pjsua_conf_port_info pjsua_conf_port_info; /***************************************************************************** @@ -478,12 +506,12 @@ PJ_DECL(pj_status_t) pjsua_set_call_media_transport(unsigned call_index, /** * Get maximum number of calls configured in pjsua. */ -PJ_DECL(unsigned) pjsua_get_max_calls(void); +PJ_DECL(unsigned) pjsua_call_get_max_count(void); /** * Get current number of active calls. */ -PJ_DECL(unsigned) pjsua_get_call_count(void); +PJ_DECL(unsigned) pjsua_call_get_count(void); /** * Check if the specified call has active INVITE session and the INVITE @@ -501,14 +529,14 @@ PJ_DECL(pj_bool_t) pjsua_call_has_media(unsigned call_index); /** * Get call info. */ -PJ_DECL(pj_status_t) pjsua_get_call_info(unsigned call_index, +PJ_DECL(pj_status_t) pjsua_call_get_info(unsigned call_index, pjsua_call_info *info); /** * Duplicate call info. */ -PJ_DECL(void) pjsua_dup_call_info(pj_pool_t *pool, +PJ_DECL(void) pjsua_call_info_dup(pj_pool_t *pool, pjsua_call_info *dst_info, const pjsua_call_info *src_info); @@ -516,15 +544,15 @@ PJ_DECL(void) pjsua_dup_call_info(pj_pool_t *pool, /** * Make outgoing call. */ -PJ_DECL(pj_status_t) pjsua_make_call(unsigned acc_index, - const pj_str_t *dst_uri, - int *p_call_index); +PJ_DECL(pj_status_t) pjsua_call_make_call(unsigned acc_id, + const pj_str_t *dst_uri, + int *p_call_index); /** * Answer call. */ -PJ_DECL(void) pjsua_call_answer(int call_index, int code); +PJ_DECL(pj_status_t) pjsua_call_answer(int call_index, int code); /** * Hangup call. @@ -535,19 +563,19 @@ PJ_DECL(void) pjsua_call_hangup(int call_index); /** * Put call on-hold. */ -PJ_DECL(void) pjsua_call_set_hold(int call_index); +PJ_DECL(pj_status_t) pjsua_call_set_hold(int call_index); /** * Send re-INVITE (to release hold). */ -PJ_DECL(void) pjsua_call_reinvite(int call_index); +PJ_DECL(pj_status_t) pjsua_call_reinvite(int call_index); /** * Transfer call. */ -PJ_DECL(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest); +PJ_DECL(pj_status_t) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest); /** * Dial DTMF. @@ -559,13 +587,14 @@ PJ_DECL(pj_status_t) pjsua_call_dial_dtmf(unsigned call_index, /** * Send instant messaging inside INVITE session. */ -PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *text); +PJ_DECL(pj_status_t) pjsua_call_send_im(int call_index, const pj_str_t *text); /** * Send IM typing indication inside INVITE session. */ -PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing); +PJ_DECL(pj_status_t) pjsua_call_send_typing_ind(int call_index, + pj_bool_t is_typing); /** * Terminate all calls. @@ -576,7 +605,7 @@ PJ_DECL(void) pjsua_call_hangup_all(void); /** * Dump call and media statistics to string. */ -PJ_DECL(void) pjsua_dump_call(int call_index, int with_media, +PJ_DECL(void) pjsua_call_dump(int call_index, int with_media, char *buffer, unsigned maxlen, const char *indent); @@ -594,9 +623,34 @@ PJ_DECL(unsigned) pjsua_get_acc_count(void); /** * Get account info. */ -PJ_DECL(pj_status_t) pjsua_acc_get_info(unsigned acc_index, +PJ_DECL(pj_status_t) pjsua_acc_get_info(pjsua_acc_id acc_id, pjsua_acc_info *info); + +/** + * Enum accounts id. + */ +PJ_DECL(pj_status_t) pjsua_acc_enum_id( pjsua_acc_id ids[], + unsigned *count ); + + +/** + * Enum accounts info. + */ +PJ_DECL(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[], + unsigned *count ); + + +/** + * Find account for outgoing request. + */ +PJ_DECL(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *url); + +/** + * Find account for incoming request. + */ +PJ_DECL(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata); + /** * Add a new account. * This function should be called after pjsua_init(). @@ -604,14 +658,18 @@ PJ_DECL(pj_status_t) pjsua_acc_get_info(unsigned acc_index, * registration for this account. */ PJ_DECL(pj_status_t) pjsua_acc_add(const pjsua_acc_config *cfg, - int *acc_index); + pjsua_acc_id *acc_id); + +/** + * Delete account. + */ +PJ_DECL(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_id); /** * Set account's presence status. - * Must call pjsua_pres_refresh() after this. */ -PJ_DECL(pj_status_t) pjsua_acc_set_online_status(unsigned acc_index, +PJ_DECL(pj_status_t) pjsua_acc_set_online_status(pjsua_acc_id acc_id, pj_bool_t is_online); @@ -619,7 +677,8 @@ PJ_DECL(pj_status_t) pjsua_acc_set_online_status(unsigned acc_index, * Update registration or perform unregistration. If renew argument is zero, * this will start unregistration process. */ -PJ_DECL(pj_status_t) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew); +PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_id, + pj_bool_t renew); @@ -637,28 +696,28 @@ PJ_DECL(unsigned) pjsua_get_buddy_count(void); /** * Get buddy info. */ -PJ_DECL(pj_status_t) pjsua_buddy_get_info(unsigned buddy_index, +PJ_DECL(pj_status_t) pjsua_buddy_get_info(pjsua_buddy_id buddy_index, pjsua_buddy_info *info); /** * Add new buddy. */ PJ_DECL(pj_status_t) pjsua_buddy_add(const pj_str_t *uri, - int *buddy_index); + pjsua_buddy_id *buddy_index); /** - * Enable/disable buddy's presence monitoring. - * Must call pjsua_pres_refresh() after this. + * Delete buddy. */ -PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(unsigned buddy_index, - pj_bool_t monitor); +PJ_DECL(pj_status_t) pjsua_buddy_del(pjsua_buddy_id buddy_index); /** - * Refresh both presence client and server subscriptions. + * Enable/disable buddy's presence monitoring. */ -PJ_DECL(void) pjsua_pres_refresh(void); +PJ_DECL(pj_status_t) pjsua_buddy_subscribe_pres(pjsua_buddy_id buddy_index, + pj_bool_t monitor); + /** * Dump presence subscriptions. @@ -680,14 +739,14 @@ extern const pjsip_method pjsip_message_method; /** * Send IM outside dialog. */ -PJ_DECL(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, +PJ_DECL(pj_status_t) pjsua_im_send(int acc_id, const pj_str_t *dst_uri, const pj_str_t *text); /** * Send typing indication outside dialog. */ -PJ_DECL(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, +PJ_DECL(pj_status_t) pjsua_im_typing(int acc_id, const pj_str_t *dst_uri, pj_bool_t is_typing); @@ -705,22 +764,29 @@ PJ_DECL(unsigned) pjsua_conf_max_ports(void); /** * Enum all conference ports. */ -PJ_DECL(pj_status_t) pjsua_conf_enum_ports(unsigned *count, - pjmedia_conf_port_info info[]); +PJ_DECL(pj_status_t) pjsua_conf_enum_port_ids(pjsua_conf_port_id id[], + unsigned *count); + + +/** + * Get information about the specified conference port + */ +PJ_DECL(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, + pjsua_conf_port_info *info); /** * Connect conference port. */ -PJ_DECL(pj_status_t) pjsua_conf_connect(unsigned src_port, - unsigned dst_port); +PJ_DECL(pj_status_t) pjsua_conf_connect(pjsua_conf_port_id src_port, + pjsua_conf_port_id dst_port); /** * Connect conference port connection. */ -PJ_DECL(pj_status_t) pjsua_conf_disconnect(unsigned src_port, - unsigned dst_port); +PJ_DECL(pj_status_t) pjsua_conf_disconnect(pjsua_conf_port_id src_port, + pjsua_conf_port_id dst_port); /** @@ -733,7 +799,7 @@ PJ_DECL(pj_status_t) pjsua_player_create(const pj_str_t *filename, /** * Get conference port associated with player. */ -PJ_DECL(int) pjsua_player_get_conf_port(pjsua_player_id id); +PJ_DECL(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id); /** @@ -760,7 +826,7 @@ PJ_DECL(pj_status_t) pjsua_recorder_create(const pj_str_t *filename, /** * Get conference port associated with recorder. */ -PJ_DECL(int) pjsua_recorder_get_conf_port(pjsua_recorder_id id); +PJ_DECL(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id); /** @@ -797,18 +863,21 @@ extern const char *pjsua_inv_state_names[]; * Parse arguments (pjsua_opt.c). */ PJ_DECL(pj_status_t) pjsua_parse_args(int argc, char *argv[], - pjsua_config *cfg); + pjsua_config *cfg, + pj_str_t *uri_to_call); /** * Load settings from a file. */ PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename, - pjsua_config *cfg); + pjsua_config *cfg, + pj_str_t *uri_to_call); /** * Get pjsua running config. */ -PJ_DECL(const pjsua_config*) pjsua_get_config(void); +PJ_DECL(void) pjsua_get_config(pj_pool_t *pool, + pjsua_config *config); /** diff --git a/pjsip/include/pjsua-lib/pjsua_console_app.h b/pjsip/include/pjsua-lib/pjsua_console_app.h index ee7213ed..8be93714 100644 --- a/pjsip/include/pjsua-lib/pjsua_console_app.h +++ b/pjsip/include/pjsua-lib/pjsua_console_app.h @@ -20,7 +20,7 @@ #define __PJSUA_CONSOLE_APP_H__ -void pjsua_console_app_main(void); +void pjsua_console_app_main(const pj_str_t *uri_to_call); extern pjsip_module pjsua_console_app_msg_logger; extern pjsua_callback console_callback; diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c index 70d9a088..507e3c93 100644 --- a/pjsip/src/pjsua-lib/pjsua_call.c +++ b/pjsip/src/pjsua-lib/pjsua_call.c @@ -127,7 +127,7 @@ static pj_status_t call_destroy_media(int call_index) /** * Get maximum number of calls configured in pjsua. */ -PJ_DEF(unsigned) pjsua_get_max_calls(void) +PJ_DEF(unsigned) pjsua_call_get_max_count(void) { return pjsua.config.max_calls; } @@ -136,7 +136,7 @@ PJ_DEF(unsigned) pjsua_get_max_calls(void) /** * Get current number of active calls. */ -PJ_DEF(unsigned) pjsua_get_call_count(void) +PJ_DEF(unsigned) pjsua_call_get_count(void) { return pjsua.call_cnt; } @@ -166,7 +166,7 @@ PJ_DEF(pj_bool_t) pjsua_call_has_media(unsigned call_index) /** * Get call info. */ -PJ_DEF(pj_status_t) pjsua_get_call_info( unsigned call_index, +PJ_DEF(pj_status_t) pjsua_call_get_info( unsigned call_index, pjsua_call_info *info) { pjsua_call *call; @@ -212,8 +212,8 @@ PJ_DEF(pj_status_t) pjsua_get_call_info( unsigned call_index, PJ_TIME_VAL_SUB(info->total_duration, call->start_time); } - info->cause = call->inv->cause; - info->cause_text = *pjsip_get_status_text(info->cause); + info->last_status = call->last_code; + info->last_status_text = *pjsip_get_status_text(info->last_status); info->has_media = (call->session != NULL); info->conf_slot = call->conf_slot; @@ -225,7 +225,7 @@ PJ_DEF(pj_status_t) pjsua_get_call_info( unsigned call_index, /** * Duplicate call info. */ -PJ_DEF(void) pjsua_dup_call_info( pj_pool_t *pool, +PJ_DEF(void) pjsua_call_info_dup( pj_pool_t *pool, pjsua_call_info *dst_info, const pjsua_call_info *src_info) { @@ -245,9 +245,9 @@ PJ_DEF(void) pjsua_dup_call_info( pj_pool_t *pool, /** * Make outgoing call. */ -PJ_DEF(pj_status_t) pjsua_make_call(unsigned acc_index, - const pj_str_t *dest_uri, - int *p_call_index) +PJ_DEF(pj_status_t) pjsua_call_make_call(unsigned acc_index, + const pj_str_t *dest_uri, + int *p_call_index) { pjsip_dialog *dlg = NULL; pjmedia_sdp_session *offer; @@ -384,18 +384,18 @@ on_error: /** * Answer call. */ -PJ_DEF(void) pjsua_call_answer(int call_index, int code) +PJ_DEF(pj_status_t) pjsua_call_answer(int call_index, int code) { pjsip_tx_data *tdata; pj_status_t status; - PJ_ASSERT_ON_FAIL(call_index >= 0 && + PJ_ASSERT_RETURN( call_index >= 0 && call_index < (int)pjsua.config.max_calls, - return); + PJ_EINVAL); if (pjsua.calls[call_index].inv == NULL) { PJ_LOG(3,(THIS_FILE, "Call %d already disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } status = pjsip_inv_answer(pjsua.calls[call_index].inv, @@ -408,6 +408,7 @@ PJ_DEF(void) pjsua_call_answer(int call_index, int code) pjsua_perror(THIS_FILE, "Unable to create/send response", status); + return status; } @@ -612,6 +613,11 @@ pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata) pjsua.config.uas_duration); } + /* Notify application */ + if (pjsua.cb.on_incoming_call) + pjsua.cb.on_incoming_call(acc_index, call_index, rdata); + + /* This INVITE request has been handled. */ return PJ_TRUE; } @@ -635,17 +641,19 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, case PJSIP_INV_STATE_CONNECTING: if (call->res_time.sec == 0) pj_gettimeofday(&call->res_time); + call->last_code = e->body.tsx_state.tsx->status_code; break; case PJSIP_INV_STATE_CONFIRMED: pj_gettimeofday(&call->conn_time); break; case PJSIP_INV_STATE_DISCONNECTED: pj_gettimeofday(&call->dis_time); + if (e->body.tsx_state.tsx->status_code > call->last_code) { + call->last_code = e->body.tsx_state.tsx->status_code; + } break; default: - /* Nothing to do. Just to keep gcc from complaining about - * unused enums. - */ + call->last_code = e->body.tsx_state.tsx->status_code; break; } @@ -682,7 +690,7 @@ static void pjsua_call_on_state_changed(pjsip_inv_session *inv, ev_state = PJSIP_EVSUB_STATE_TERMINATED; break; - default: + case PJSIP_INV_STATE_INCOMING: /* Nothing to do. Just to keep gcc from complaining about * unused enums. */ @@ -853,7 +861,7 @@ static void on_call_transfered( pjsip_inv_session *inv, /* Now make the outgoing call. */ tmp = pj_str(uri); - status = pjsua_make_call(existing_call->acc_index, &tmp, &new_call); + status = pjsua_call_make_call(existing_call->acc_index, &tmp, &new_call); if (status != PJ_SUCCESS) { /* Notify xferer about the error */ @@ -1354,7 +1362,7 @@ PJ_DEF(void) pjsua_call_hangup(int call_index) /* * Put call on-Hold. */ -PJ_DEF(void) pjsua_call_set_hold(int call_index) +PJ_DEF(pj_status_t) pjsua_call_set_hold(int call_index) { pjmedia_sdp_session *sdp; pjsua_call *call; @@ -1365,37 +1373,39 @@ PJ_DEF(void) pjsua_call_set_hold(int call_index) if (!call->inv) { PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { PJ_LOG(3,(THIS_FILE, "Can not hold call that is not confirmed")); - return; + return PJSIP_ESESSIONSTATE; } status = create_inactive_sdp(call, &sdp); if (status != PJ_SUCCESS) - return; + return status; /* Send re-INVITE with new offer */ status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); - return; + return status; } status = pjsip_inv_send_msg( call->inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); - return; + return status; } + + return PJ_SUCCESS; } /* * re-INVITE. */ -PJ_DEF(void) pjsua_call_reinvite(int call_index) +PJ_DEF(pj_status_t) pjsua_call_reinvite(int call_index) { pjmedia_sdp_session *sdp; pjsip_tx_data *tdata; @@ -1406,13 +1416,13 @@ PJ_DEF(void) pjsua_call_reinvite(int call_index) if (!call->inv) { PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } if (call->inv->state != PJSIP_INV_STATE_CONFIRMED) { PJ_LOG(3,(THIS_FILE, "Can not re-INVITE call that is not confirmed")); - return; + return PJSIP_ESESSIONSTATE; } /* Create SDP */ @@ -1421,28 +1431,30 @@ PJ_DEF(void) pjsua_call_reinvite(int call_index) if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", status); - return; + return status; } /* Send re-INVITE with new offer */ status = pjsip_inv_reinvite( call->inv, NULL, sdp, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create re-INVITE", status); - return; + return status; } status = pjsip_inv_send_msg( call->inv, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send re-INVITE", status); - return; + return status; } + + return PJ_SUCCESS; } /* * Transfer call. */ -PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) +PJ_DEF(pj_status_t) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) { pjsip_evsub *sub; pjsip_tx_data *tdata; @@ -1454,7 +1466,7 @@ PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) if (!call->inv) { PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } /* Create xfer client subscription. @@ -1464,7 +1476,7 @@ PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) status = pjsip_xfer_create_uac(call->inv->dlg, NULL, &sub); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create xfer", status); - return; + return status; } /* @@ -1473,20 +1485,22 @@ PJ_DEF(void) pjsua_call_xfer(unsigned call_index, const pj_str_t *dest) status = pjsip_xfer_initiate(sub, dest, &tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to create REFER request", status); - return; + return status; } /* Send. */ status = pjsip_xfer_send_request(sub, tdata); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Unable to send REFER request", status); - return; + return status; } /* For simplicity (that's what this program is intended to be!), * leave the original invite session as it is. More advanced application * may want to hold the INVITE, or terminate the invite, or whatever. */ + + return PJ_SUCCESS; } @@ -1512,7 +1526,7 @@ PJ_DEF(pj_status_t) pjsua_call_dial_dtmf( unsigned call_index, /** * Send instant messaging inside INVITE session. */ -PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *str) +PJ_DEF(pj_status_t) pjsua_call_send_im(int call_index, const pj_str_t *str) { pjsua_call *call; const pj_str_t mime_text = pj_str("text"); @@ -1524,7 +1538,7 @@ PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *str) if (!call->inv) { PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } /* Lock dialog. */ @@ -1560,13 +1574,15 @@ PJ_DECL(void) pjsua_call_send_im(int call_index, const pj_str_t *str) on_return: pjsip_dlg_dec_lock(call->inv->dlg); + return status; } /** * Send IM typing indication inside INVITE session. */ -PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing) +PJ_DEF(pj_status_t) pjsua_call_send_typing_ind(int call_index, + pj_bool_t is_typing) { pjsua_call *call; pjsip_tx_data *tdata; @@ -1576,7 +1592,7 @@ PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing) if (!call->inv) { PJ_LOG(3,(THIS_FILE,"Call has been disconnected")); - return; + return PJSIP_ESESSIONTERMINATED; } /* Lock dialog. */ @@ -1602,7 +1618,9 @@ PJ_DECL(void) pjsua_call_typing(int call_index, pj_bool_t is_typing) } on_return: - pjsip_dlg_dec_lock(call->inv->dlg);} + pjsip_dlg_dec_lock(call->inv->dlg); + return status; +} /* diff --git a/pjsip/src/pjsua-lib/pjsua_console_app.c b/pjsip/src/pjsua-lib/pjsua_console_app.c index 58f64c7f..a54dd689 100644 --- a/pjsip/src/pjsua-lib/pjsua_console_app.c +++ b/pjsip/src/pjsua-lib/pjsua_console_app.c @@ -36,7 +36,7 @@ static pj_bool_t find_next_call(void) { int i, max; - max = pjsua_get_max_calls(); + max = pjsua_call_get_max_count(); for (i=current_call+1; i<max; ++i) { if (pjsua_call_is_active(i)) { current_call = i; @@ -63,7 +63,7 @@ static pj_bool_t find_prev_call(void) { int i, max; - max = pjsua_get_max_calls(); + max = pjsua_call_get_max_count(); for (i=current_call-1; i>=0; --i) { if (pjsua_call_is_active(i)) { current_call = i; @@ -93,14 +93,14 @@ static void console_on_call_state(int call_index, pjsip_event *e) PJ_UNUSED_ARG(e); - pjsua_get_call_info(call_index, &call_info); + pjsua_call_get_info(call_index, &call_info); if (call_info.state == PJSIP_INV_STATE_DISCONNECTED) { PJ_LOG(3,(THIS_FILE, "Call %d is DISCONNECTED [reason=%d (%s)]", call_index, - call_info.cause, - call_info.cause_text.ptr)); + call_info.last_status, + call_info.last_status_text.ptr)); if ((int)call_index == current_call) { find_next_call(); @@ -385,31 +385,32 @@ static void ui_input_url(const char *title, char *buf, int len, static void conf_list(void) { unsigned i, count; - pjmedia_conf_port_info info[PJSUA_MAX_CALLS]; + pjsua_conf_port_id id[PJSUA_MAX_CALLS]; printf("Conference ports:\n"); - count = PJ_ARRAY_SIZE(info); - pjsua_conf_enum_ports(&count, info); + count = PJ_ARRAY_SIZE(id); + pjsua_conf_enum_port_ids(id, &count); + for (i=0; i<count; ++i) { char txlist[PJSUA_MAX_CALLS*4+10]; - int j; - pjmedia_conf_port_info *port_info = &info[i]; - + unsigned j; + pjsua_conf_port_info info; + + pjsua_conf_get_port_info(id[i], &info); + txlist[0] = '\0'; - for (j=0; j<(int)count; ++j) { + for (j=0; j<info.listener_cnt; ++j) { char s[10]; - if (port_info->listener[j]) { - pj_ansi_sprintf(s, "#%d ", j); - pj_ansi_strcat(txlist, s); - } + pj_ansi_sprintf(s, "#%d ", info.listeners[j]); + pj_ansi_strcat(txlist, s); } printf("Port #%02d[%2dKHz/%dms] %20.*s transmitting to: %s\n", - port_info->slot, - port_info->clock_rate/1000, - port_info->samples_per_frame * 1000 / port_info->clock_rate, - (int)port_info->name.slen, - port_info->name.ptr, + info.slot_id, + info.clock_rate/1000, + info.samples_per_frame * 1000 / info.clock_rate, + (int)info.name.slen, + info.name.ptr, txlist); } @@ -417,7 +418,7 @@ static void conf_list(void) } -void pjsua_console_app_main(void) +void pjsua_console_app_main(const pj_str_t *uri_to_call) { char menuin[10]; char buf[128]; @@ -431,9 +432,8 @@ void pjsua_console_app_main(void) /* If user specifies URI to call, then call the URI */ - if (pjsua_get_config()->uri_to_call.slen) { - pjsua_make_call( current_acc, &pjsua_get_config()->uri_to_call, - NULL); + if (uri_to_call->slen) { + pjsua_call_make_call( current_acc, uri_to_call, NULL); } keystroke_help(); @@ -449,7 +449,8 @@ void pjsua_console_app_main(void) case 'm': /* Make call! : */ - printf("(You currently have %d calls)\n", pjsua_get_call_count()); + printf("(You currently have %d calls)\n", + pjsua_call_get_count()); uri = NULL; ui_input_url("Make call", buf, sizeof(buf), &result); @@ -469,12 +470,13 @@ void pjsua_console_app_main(void) } tmp = pj_str(uri); - pjsua_make_call( current_acc, &tmp, NULL); + pjsua_call_make_call( current_acc, &tmp, NULL); break; case 'M': /* Make multiple calls! : */ - printf("(You currently have %d calls)\n", pjsua_get_call_count()); + printf("(You currently have %d calls)\n", + pjsua_call_get_count()); if (!simple_input("Number of calls", menuin, sizeof(menuin))) continue; @@ -500,7 +502,7 @@ void pjsua_console_app_main(void) pj_status_t status; tmp = pj_str(uri); - status = pjsua_make_call(current_acc, &tmp, NULL); + status = pjsua_call_make_call(current_acc, &tmp, NULL); if (status != PJ_SUCCESS) break; } @@ -540,7 +542,7 @@ void pjsua_console_app_main(void) /* Send typing indication. */ if (i != -1) - pjsua_call_typing(i, PJ_TRUE); + pjsua_call_send_typing_ind(i, PJ_TRUE); else { pj_str_t tmp_uri = pj_str(uri); pjsua_im_typing(current_acc, &tmp_uri, PJ_TRUE); @@ -553,7 +555,7 @@ void pjsua_console_app_main(void) * Send typing notification too, saying we're not typing. */ if (i != -1) - pjsua_call_typing(i, PJ_FALSE); + pjsua_call_send_typing_ind(i, PJ_FALSE); else { pj_str_t tmp_uri = pj_str(uri); pjsua_im_typing(current_acc, &tmp_uri, PJ_FALSE); @@ -576,7 +578,7 @@ void pjsua_console_app_main(void) case 'a': if (current_call != -1) { - pjsua_get_call_info(current_call, &call_info); + pjsua_call_get_info(current_call, &call_info); } else { /* Make compiler happy */ call_info.active = 0; @@ -650,7 +652,7 @@ void pjsua_console_app_main(void) if (current_call != -1) { - pjsua_get_call_info(current_call, &call_info); + pjsua_call_get_info(current_call, &call_info); PJ_LOG(3,(THIS_FILE,"Current dialog: %.*s", (int)call_info.remote_info.slen, call_info.remote_info.ptr)); @@ -781,8 +783,6 @@ void pjsua_console_app_main(void) pjsua_buddy_subscribe_pres(result.nb_result-1, (menuin[0]=='s')); } - pjsua_pres_refresh(); - } else if (result.uri_result) { puts("Sorry, can only subscribe to buddy's presence, " "not arbitrary URL (for now)"); @@ -814,7 +814,6 @@ void pjsua_console_app_main(void) printf("Setting %s online status to %s\n", acc_info.acc_id.ptr, (acc_info.online_status?"online":"offline")); - pjsua_pres_refresh(); break; case 'c': @@ -897,80 +896,6 @@ on_exit: /***************************************************************************** - * This is a very simple PJSIP module, whose sole purpose is to display - * incoming and outgoing messages to log. This module will have priority - * higher than transport layer, which means: - * - * - incoming messages will come to this module first before reaching - * transaction layer. - * - * - outgoing messages will come to this module last, after the message - * has been 'printed' to contiguous buffer by transport layer and - * appropriate transport instance has been decided for this message. - * - */ - -/* Notification on incoming messages */ -static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata) -{ - PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n" - "%s\n" - "--end msg--", - rdata->msg_info.len, - pjsip_rx_data_get_info(rdata), - rdata->pkt_info.src_name, - rdata->pkt_info.src_port, - rdata->msg_info.msg_buf)); - - /* Always return false, otherwise messages will not get processed! */ - return PJ_FALSE; -} - -/* Notification on outgoing messages */ -static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata) -{ - - /* Important note: - * tp_info field is only valid after outgoing messages has passed - * transport layer. So don't try to access tp_info when the module - * has lower priority than transport layer. - */ - - PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n" - "%s\n" - "--end msg--", - (tdata->buf.cur - tdata->buf.start), - pjsip_tx_data_get_info(tdata), - tdata->tp_info.dst_name, - tdata->tp_info.dst_port, - tdata->buf.start)); - - /* Always return success, otherwise message will not get sent! */ - return PJ_SUCCESS; -} - -/* The module instance. */ -pjsip_module pjsua_console_app_msg_logger = -{ - NULL, NULL, /* prev, next. */ - { "mod-pjsua-log", 13 }, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - &console_on_rx_msg, /* on_rx_request() */ - &console_on_rx_msg, /* on_rx_response() */ - &console_on_tx_msg, /* on_tx_request. */ - &console_on_tx_msg, /* on_tx_response() */ - NULL, /* on_tsx_state() */ - -}; - - - -/***************************************************************************** * Error display: */ @@ -992,7 +917,8 @@ void pjsua_perror(const char *sender, const char *title, pjsua_callback console_callback = { &console_on_call_state, - NULL, /* default accept transfer */ + NULL, /* on_incoming_call */ + NULL, /* default accept transfer */ &console_on_reg_state, &console_on_buddy_state, &console_on_pager, diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c index 3f63ce8f..01b6c8c4 100644 --- a/pjsip/src/pjsua-lib/pjsua_core.c +++ b/pjsip/src/pjsua-lib/pjsua_core.c @@ -55,6 +55,7 @@ PJ_DEF(void) pjsua_default_config(pjsua_config *cfg) cfg->media_thread_cnt = 1; cfg->udp_port = 5060; cfg->start_rtp_port = 4000; + cfg->msg_logging = PJ_TRUE; cfg->max_calls = 4; cfg->conf_ports = 0; @@ -721,6 +722,8 @@ static void copy_acc_config(pj_pool_t *pool, { unsigned j; + pj_memcpy(dst_acc, src_acc, sizeof(pjsua_acc_config)); + pj_strdup_with_null(pool, &dst_acc->id, &src_acc->id); pj_strdup_with_null(pool, &dst_acc->reg_uri, &src_acc->reg_uri); pj_strdup_with_null(pool, &dst_acc->contact, &src_acc->contact); @@ -742,7 +745,7 @@ static void copy_acc_config(pj_pool_t *pool, /* * Copy configuration. */ -static void copy_config(pj_pool_t *pool, pjsua_config *dst, +void pjsua_copy_config( pj_pool_t *pool, pjsua_config *dst, const pjsua_config *src) { unsigned i; @@ -761,7 +764,7 @@ static void copy_config(pj_pool_t *pool, pjsua_config *dst, } pj_strdup_with_null(pool, &dst->outbound_proxy, &src->outbound_proxy); - pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call); + //pj_strdup_with_null(pool, &dst->uri_to_call, &src->uri_to_call); for (i=0; i<src->acc_cnt; ++i) { pjsua_acc_config *dst_acc = &dst->acc_config[i]; @@ -813,6 +816,10 @@ static pj_status_t logging_init() } } + /* Enable SIP message logging */ + if (pjsua.config.msg_logging) + pjsip_endpt_register_module(pjsua.endpt, &pjsua_msg_logger); + return PJ_SUCCESS; } @@ -863,7 +870,7 @@ PJ_DECL(pj_status_t) pjsua_init(const pjsua_config *cfg, } /* Copy configuration */ - copy_config(pjsua.pool, &pjsua.config, cfg); + pjsua_copy_config(pjsua.pool, &pjsua.config, cfg); /* Copy callback */ pj_memcpy(&pjsua.cb, cb, sizeof(pjsua_callback)); @@ -1026,7 +1033,7 @@ on_error: /* * Find account for incoming request. */ -int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) +PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_incoming(pjsip_rx_data *rdata) { pjsip_uri *uri; pjsip_sip_uri *sip_uri; @@ -1034,11 +1041,11 @@ int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) uri = rdata->msg_info.to->uri; - /* Just return last account if To URI is not SIP: */ + /* Just return default account if To URI is not SIP: */ if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) { - return pjsua.config.acc_cnt; + return pjsua.default_acc; } @@ -1068,20 +1075,60 @@ int pjsua_find_account_for_incoming(pjsip_rx_data *rdata) } } - /* Still no match, just return last account */ - return pjsua.config.acc_cnt; + /* Still no match, use default account */ + return pjsua.default_acc; } /* * Find account for outgoing request. */ -int pjsua_find_account_for_outgoing(const pj_str_t *url) +PJ_DEF(pjsua_acc_id) pjsua_acc_find_for_outgoing(const pj_str_t *str_url) { - PJ_UNUSED_ARG(url); + pj_str_t tmp; + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + unsigned i; - /* Just use account #0 */ - return 0; + pj_strdup_with_null(pjsua.pool, &tmp, str_url); + + uri = pjsip_parse_uri(pjsua.pool, tmp.ptr, tmp.slen, 0); + if (!uri) + return pjsua.config.acc_cnt-1; + + if (!PJSIP_URI_SCHEME_IS_SIP(uri) && + !PJSIP_URI_SCHEME_IS_SIPS(uri)) + { + /* Return the first account with proxy */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + if (!pjsua.acc[i].valid) + continue; + if (pjsua.config.acc_config[i].proxy.slen) + break; + } + + if (i != PJ_ARRAY_SIZE(pjsua.acc)) + return i; + + /* Not found, use default account */ + return pjsua.default_acc; + } + + sip_uri = pjsip_uri_get_uri(uri); + + /* Find matching domain */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + if (!pjsua.acc[i].valid) + continue; + if (pj_stricmp(&pjsua.acc[i].host_part, &sip_uri->host)==0) + break; + } + + if (i != PJ_ARRAY_SIZE(pjsua.acc)) + return i; + + /* Just use default account */ + return pjsua.default_acc; } @@ -1192,6 +1239,10 @@ static pj_status_t init_acc(unsigned acc_index) pj_list_push_back(&acc->route_set, r); } + /* Mark account as valid */ + pjsua.acc[acc_index].valid = PJ_TRUE; + + return PJ_SUCCESS; } @@ -1199,23 +1250,34 @@ static pj_status_t init_acc(unsigned acc_index) * Add a new account. */ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, - int *acc_index) + pjsua_acc_id *acc_index) { + unsigned index; pj_status_t status; - PJ_ASSERT_RETURN(pjsua.config.acc_cnt<PJ_ARRAY_SIZE(pjsua.config.acc_config), + PJ_ASSERT_RETURN(pjsua.config.acc_cnt < + PJ_ARRAY_SIZE(pjsua.config.acc_config), PJ_ETOOMANY); - copy_acc_config(pjsua.pool, &pjsua.config.acc_config[pjsua.config.acc_cnt], cfg); + /* Find empty account index. */ + for (index=0; index < PJ_ARRAY_SIZE(pjsua.acc); ++index) { + if (pjsua.acc[index].valid == PJ_FALSE) + break; + } + + /* Expect to find a slot */ + PJ_ASSERT_RETURN(index < PJ_ARRAY_SIZE(pjsua.acc), PJ_EBUG); + + copy_acc_config(pjsua.pool, &pjsua.config.acc_config[index], cfg); - status = init_acc(pjsua.config.acc_cnt); + status = init_acc(index); if (status != PJ_SUCCESS) { pjsua_perror(THIS_FILE, "Error adding account", status); return status; } if (acc_index) - *acc_index = pjsua.config.acc_cnt; + *acc_index = index; pjsua.config.acc_cnt++; @@ -1223,6 +1285,25 @@ PJ_DEF(pj_status_t) pjsua_acc_add( const pjsua_acc_config *cfg, } +/* + * Delete account. + */ +PJ_DEF(pj_status_t) pjsua_acc_del(pjsua_acc_id acc_index) +{ + PJ_ASSERT_RETURN(acc_index < (int)pjsua.config.acc_cnt, + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP); + + /* Delete registration */ + if (pjsua.acc[acc_index].regc != NULL) + pjsua_acc_set_registration(acc_index, PJ_FALSE); + + /* Invalidate */ + pjsua.acc[acc_index].valid = PJ_FALSE; + + return PJ_SUCCESS; +} + /* * Start pjsua stack. @@ -1266,7 +1347,7 @@ PJ_DEF(pj_status_t) pjsua_start(void) pj_str_t tmp, id; tmp.ptr = buf; - tmp.slen = pj_ansi_sprintf(tmp.ptr, "<sip:%s:%d>", + tmp.slen = pj_ansi_sprintf(tmp.ptr, "Local <sip:%s:%d>", pjsua.config.sip_host.ptr, pjsua.config.sip_port); pj_strdup_with_null( pjsua.pool, &id, &tmp); @@ -1283,6 +1364,17 @@ PJ_DEF(pj_status_t) pjsua_start(void) } + /* Add another account as the last one. + * This account corresponds to local endpoint, and is user-less. + * This is also the default account. + */ + if (pjsua.config.acc_cnt < PJ_ARRAY_SIZE(pjsua.config.acc_config)) { + pjsua.default_acc = pjsua.config.acc_cnt; + pjsua.acc[pjsua.default_acc].auto_gen = 1; + pjsua.config.acc_cnt++; + } + + /* Initialize accounts: */ for (i=0; i<(int)pjsua.config.acc_cnt; ++i) { status = init_acc(i); @@ -1293,7 +1385,6 @@ PJ_DEF(pj_status_t) pjsua_start(void) } - /* Create worker thread(s), if required: */ for (i=0; i<(int)pjsua.config.thread_cnt; ++i) { @@ -1333,6 +1424,9 @@ PJ_DEF(pj_status_t) pjsua_start(void) } + /* Refresh presence */ + pjsua_pres_refresh(); + PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION)); return PJ_SUCCESS; @@ -1368,22 +1462,55 @@ PJ_DEF(unsigned) pjsua_conf_max_ports(void) /** - * Enum all conference ports. + * Enum all conference port ID. */ -PJ_DEF(pj_status_t) pjsua_conf_enum_ports( unsigned *count, - pjmedia_conf_port_info info[]) +PJ_DEF(pj_status_t) pjsua_conf_enum_port_ids( pjsua_conf_port_id id[], + unsigned *count) { - return pjmedia_conf_get_ports_info(pjsua.mconf, count, info); + return pjmedia_conf_enum_ports( pjsua.mconf, (unsigned*)id, count); } +/** + * Get information about the specified conference port + */ +PJ_DEF(pj_status_t) pjsua_conf_get_port_info( pjsua_conf_port_id id, + pjsua_conf_port_info *info) +{ + pjmedia_conf_port_info cinfo; + unsigned i, count; + pj_status_t status; + + status = pjmedia_conf_get_port_info( pjsua.mconf, id, &cinfo); + if (status != PJ_SUCCESS) + return status; + + pj_memset(info, 0, sizeof(*info)); + info->slot_id = id; + info->name = cinfo.name; + info->clock_rate = cinfo.clock_rate; + info->channel_count = cinfo.channel_count; + info->samples_per_frame = cinfo.samples_per_frame; + info->bits_per_sample = cinfo.bits_per_sample; + + /* Build array of listeners */ + count = pjsua.config.conf_ports; + for (i=0; i<count; ++i) { + if (cinfo.listener[i]) { + info->listeners[info->listener_cnt++] = i; + } + } + + return PJ_SUCCESS; +} + /** * Connect conference port. */ -PJ_DEF(pj_status_t) pjsua_conf_connect( unsigned src_port, - unsigned dst_port) +PJ_DEF(pj_status_t) pjsua_conf_connect( pjsua_conf_port_id src_port, + pjsua_conf_port_id dst_port) { return pjmedia_conf_connect_port(pjsua.mconf, src_port, dst_port, 0); } @@ -1392,8 +1519,8 @@ PJ_DEF(pj_status_t) pjsua_conf_connect( unsigned src_port, /** * Connect conference port connection. */ -PJ_DEF(pj_status_t) pjsua_conf_disconnect( unsigned src_port, - unsigned dst_port) +PJ_DEF(pj_status_t) pjsua_conf_disconnect( pjsua_conf_port_id src_port, + pjsua_conf_port_id dst_port) { return pjmedia_conf_disconnect_port(pjsua.mconf, src_port, dst_port); } @@ -1434,7 +1561,7 @@ PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename, pjsua.player[pjsua.player_cnt].port = port; pjsua.player[pjsua.player_cnt].slot = slot; - if (*id) + if (id) *id = pjsua.player_cnt; ++pjsua.player_cnt; @@ -1446,7 +1573,7 @@ PJ_DEF(pj_status_t) pjsua_player_create( const pj_str_t *filename, /** * Get conference port associated with player. */ -PJ_DEF(int) pjsua_player_get_conf_port(pjsua_player_id id) +PJ_DEF(pjsua_conf_port_id) pjsua_player_get_conf_port(pjsua_player_id id) { PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.player), PJ_EINVAL); return pjsua.player[id].slot; @@ -1530,7 +1657,7 @@ PJ_DEF(pj_status_t) pjsua_recorder_create( const pj_str_t *filename, /** * Get conference port associated with recorder. */ -PJ_DEF(int) pjsua_recorder_get_conf_port(pjsua_recorder_id id) +PJ_DEF(pjsua_conf_port_id) pjsua_recorder_get_conf_port(pjsua_recorder_id id) { PJ_ASSERT_RETURN(id>=0 && id < PJ_ARRAY_SIZE(pjsua.recorder), PJ_EINVAL); return pjsua.recorder[id].slot; diff --git a/pjsip/src/pjsua-lib/pjsua_im.c b/pjsip/src/pjsua-lib/pjsua_im.c index efd8cbf6..807f2fac 100644 --- a/pjsip/src/pjsua-lib/pjsua_im.c +++ b/pjsip/src/pjsua-lib/pjsua_im.c @@ -185,6 +185,7 @@ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) { pj_str_t from, to; pjsip_accept_hdr *accept_hdr; + pjsip_contact_hdr *contact_hdr; pjsip_msg *msg; pj_status_t status; @@ -221,11 +222,23 @@ static pj_bool_t im_on_rx_request(pjsip_rx_data *rdata) status = pjsip_endpt_respond( pjsua.endpt, NULL, rdata, 200, NULL, NULL, NULL, NULL); - /* Build the From text. */ + /* For the source URI, we use Contact header if present, since + * Contact header contains the port number information. If this is + * not available, then use From header. + */ from.ptr = pj_pool_alloc(rdata->tp_info.pool, PJSIP_MAX_URL_SIZE); - from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, - rdata->msg_info.from->uri, - from.ptr, PJSIP_MAX_URL_SIZE); + contact_hdr = pjsip_msg_find_hdr(rdata->msg_info.msg, + PJSIP_H_CONTACT, NULL); + if (contact_hdr) { + from.slen = pjsip_uri_print(PJSIP_URI_IN_CONTACT_HDR, + contact_hdr->uri, + from.ptr, PJSIP_MAX_URL_SIZE); + } else { + from.slen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, + rdata->msg_info.from->uri, + from.ptr, PJSIP_MAX_URL_SIZE); + } + if (from.slen < 1) from = pj_str("<--URI is too long-->"); @@ -336,6 +349,7 @@ PJ_DEF(pj_status_t) pjsua_im_send(int acc_index, const pj_str_t *dst_uri, PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, pj_bool_t is_typing) { + const pj_str_t STR_CONTACT = { "Contact", 7 }; pjsip_tx_data *tdata; pj_status_t status; @@ -350,6 +364,18 @@ PJ_DEF(pj_status_t) pjsua_im_typing(int acc_index, const pj_str_t *dst_uri, } + /* Add accept header. */ + pjsip_msg_add_hdr( tdata->msg, + (pjsip_hdr*)pjsua_im_create_accept(tdata->pool)); + + + /* Add contact. */ + pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*) + pjsip_generic_string_hdr_create(tdata->pool, + &STR_CONTACT, + &pjsua.config.acc_config[acc_index].contact)); + + /* Create "application/im-iscomposing+xml" msg body. */ tdata->msg->body = pjsip_iscomposing_create_body( tdata->pool, is_typing, NULL, NULL, -1); diff --git a/pjsip/src/pjsua-lib/pjsua_imp.h b/pjsip/src/pjsua-lib/pjsua_imp.h index ec1918d3..117a2d12 100644 --- a/pjsip/src/pjsua-lib/pjsua_imp.h +++ b/pjsip/src/pjsua-lib/pjsua_imp.h @@ -32,6 +32,7 @@ struct pjsua_call { unsigned index; /**< Index in pjsua array. */ pjsip_inv_session *inv; /**< The invite session. */ + pjsip_status_code last_code; /**<Last status code seen. */ pj_time_val start_time;/**< First INVITE sent/received. */ pj_time_val res_time; /**< First response sent/received. */ pj_time_val conn_time; /**< Connected/confirmed time. */ @@ -61,7 +62,6 @@ struct pjsua_buddy pj_str_t display; /**< Buddy display name. */ pj_str_t host; /**< Buddy host. */ unsigned port; /**< Buddy port. */ - int acc_index; /**< Which account to use. */ pj_bool_t monitor; /**< Should we monitor? */ pjsip_evsub *sub; /**< Buddy presence subscription */ pjsip_pres_status status; /**< Buddy presence status. */ @@ -89,6 +89,8 @@ typedef struct pjsua_srv_pres pjsua_srv_pres; */ struct pjsua_acc { + pj_bool_t valid; /**< Is this account valid? */ + pj_bool_t auto_gen; /**< Is this account generated. */ int index; /**< Index in accounts array. */ pj_str_t user_part; /**< User part of local URI. */ pj_str_t host_part; /**< Host part of local URI. */ @@ -159,6 +161,7 @@ struct pjsua } recorder[32]; /* Account: */ + int default_acc; /**< Default account to use. */ pjsua_acc acc[PJSUA_MAX_ACC]; /** Client regs array. */ @@ -175,7 +178,6 @@ struct pjsua unsigned call_cnt; /**< Number of calls. */ pjsua_call calls[PJSUA_MAX_CALLS]; /** Calls array. */ - /* SIMPLE and buddy status: */ pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; }; @@ -185,17 +187,8 @@ struct pjsua extern struct pjsua pjsua; - -/** - * Find account for incoming request. - */ -int pjsua_find_account_for_incoming(pjsip_rx_data *rdata); - - -/** - * Find account for outgoing request. - */ -int pjsua_find_account_for_outgoing(const pj_str_t *url); +void pjsua_copy_config( pj_pool_t *pool, pjsua_config *dst, + const pjsua_config *src); /** @@ -225,6 +218,11 @@ pj_status_t pjsua_pres_init(); /** + * Refresh both presence client and server subscriptions. + */ +void pjsua_pres_refresh(void); + +/** * Terminate all subscriptions */ void pjsua_pres_shutdown(void); @@ -255,6 +253,7 @@ void pjsua_im_process_pager(int call_id, const pj_str_t *from, const pj_str_t *to, pjsip_rx_data *rdata); +extern pjsip_module pjsua_msg_logger; #endif /* __PJSUA_IMP_H__ */ diff --git a/pjsip/src/pjsua-lib/pjsua_pres.c b/pjsip/src/pjsua-lib/pjsua_pres.c index c76d3cbb..b24ffd3c 100644 --- a/pjsip/src/pjsua-lib/pjsua_pres.c +++ b/pjsip/src/pjsua-lib/pjsua_pres.c @@ -97,7 +97,7 @@ static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata) /* Incoming SUBSCRIBE: */ /* Find which account for the incoming request. */ - acc_index = pjsua_find_account_for_incoming(rdata); + acc_index = pjsua_acc_find_for_incoming(rdata); acc_config = &pjsua.config.acc_config[acc_index]; /* Create UAS dialog: */ @@ -310,7 +310,8 @@ static void subscribe_buddy_presence(unsigned index) pjsip_tx_data *tdata; pj_status_t status; - acc_index = pjsua.buddies[index].acc_index; + acc_index = pjsua_acc_find_for_outgoing(&pjsua.config.buddy_uri[index]); + acc_config = &pjsua.config.acc_config[acc_index]; status = pjsip_dlg_create_uac( pjsip_ua_instance(), @@ -439,12 +440,13 @@ PJ_DEF(unsigned) pjsua_get_buddy_count(void) /** * Get buddy info. */ -PJ_DEF(pj_status_t) pjsua_buddy_get_info(unsigned index, +PJ_DEF(pj_status_t) pjsua_buddy_get_info(pjsua_buddy_id index, pjsua_buddy_info *info) { pjsua_buddy *buddy; - PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_EINVAL); pj_memset(info, 0, sizeof(pjsua_buddy_info)); @@ -471,7 +473,6 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info(unsigned index, info->status_text = pj_str("Offline"); } - info->acc_index = buddy->acc_index; return PJ_SUCCESS; } @@ -480,17 +481,27 @@ PJ_DEF(pj_status_t) pjsua_buddy_get_info(unsigned index, * Add new buddy. */ PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri, - int *buddy_index) + pjsua_buddy_id *buddy_index) { pjsip_name_addr *url; pjsip_sip_uri *sip_uri; int index; pj_str_t tmp; - PJ_ASSERT_RETURN(pjsua.config.buddy_cnt <= PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_ASSERT_RETURN(pjsua.config.buddy_cnt <= + PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_ETOOMANY); + + /* Find empty slot */ + for (index=0; index<PJ_ARRAY_SIZE(pjsua.config.buddy_uri); ++index) { + if (pjsua.config.buddy_uri[index].slen == 0) + break; + } + + /* Expect to find an empty slot */ + PJ_ASSERT_RETURN(index < PJ_ARRAY_SIZE(pjsua.config.buddy_uri), PJ_ETOOMANY); - index = pjsua.config.buddy_cnt; /* Get name and display name for buddy */ pj_strdup_with_null(pjsua.pool, &tmp, uri); @@ -511,10 +522,6 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri, if (pjsua.buddies[index].port == 0) pjsua.buddies[index].port = 5060; - /* Find account for outgoing preence subscription */ - pjsua.buddies[index].acc_index = - pjsua_find_account_for_outgoing(&pjsua.config.buddy_uri[index]); - if (buddy_index) *buddy_index = index; @@ -525,24 +532,52 @@ PJ_DEF(pj_status_t) pjsua_buddy_add( const pj_str_t *uri, -PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( unsigned index, +/** + * Delete buddy. + */ +PJ_DEF(pj_status_t) pjsua_buddy_del(pjsua_buddy_id index) +{ + PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_EINVAL); + + if (pjsua.config.buddy_uri[index].slen == 0) + return PJ_SUCCESS; + + /* Unsubscribe presence */ + pjsua_buddy_subscribe_pres(index, PJ_FALSE); + + /* Remove buddy */ + pjsua.config.buddy_uri[index].slen = 0; + pjsua.config.buddy_cnt--; + + return PJ_SUCCESS; +} + + +PJ_DEF(pj_status_t) pjsua_buddy_subscribe_pres( pjsua_buddy_id index, pj_bool_t monitor) { pjsua_buddy *buddy; - PJ_ASSERT_RETURN(index < pjsua.config.buddy_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(index < (int)PJ_ARRAY_SIZE(pjsua.config.buddy_uri), + PJ_EINVAL); buddy = &pjsua.buddies[index]; buddy->monitor = monitor; + pjsua_pres_refresh(); return PJ_SUCCESS; } -PJ_DEF(pj_status_t) pjsua_acc_set_online_status( unsigned acc_index, +PJ_DEF(pj_status_t) pjsua_acc_set_online_status( pjsua_acc_id acc_index, pj_bool_t is_online) { - PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL); + PJ_ASSERT_RETURN(acc_index < (int)PJ_ARRAY_SIZE(pjsua.acc), + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP); + pjsua.acc[acc_index].online_status = is_online; + pjsua_pres_refresh(); return PJ_SUCCESS; } diff --git a/pjsip/src/pjsua-lib/pjsua_reg.c b/pjsip/src/pjsua-lib/pjsua_reg.c index f85bcd77..54dbcbd2 100644 --- a/pjsip/src/pjsua-lib/pjsua_reg.c +++ b/pjsip/src/pjsua-lib/pjsua_reg.c @@ -95,16 +95,21 @@ PJ_DEF(unsigned) pjsua_get_acc_count(void) /** * Get account info. */ -PJ_DEF(pj_status_t) pjsua_acc_get_info( unsigned acc_index, +PJ_DEF(pj_status_t) pjsua_acc_get_info( pjsua_acc_id acc_index, pjsua_acc_info *info) { pjsua_acc *acc = &pjsua.acc[acc_index]; pjsua_acc_config *acc_cfg = &pjsua.config.acc_config[acc_index]; - PJ_ASSERT_RETURN(acc_index < pjsua.config.acc_cnt, PJ_EINVAL); - + PJ_ASSERT_RETURN(info != NULL, PJ_EINVAL); + pj_memset(info, 0, sizeof(pjsua_acc_info)); + PJ_ASSERT_RETURN(acc_index < (int)pjsua.config.acc_cnt, + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP); + + info->index = acc_index; info->acc_id = acc_cfg->id; info->has_registration = (acc->regc != NULL); @@ -114,9 +119,12 @@ PJ_DEF(pj_status_t) pjsua_acc_get_info( unsigned acc_index, info->status = acc->reg_last_err; pj_strerror(acc->reg_last_err, info->buf, sizeof(info->buf)); info->status_text = pj_str(info->buf); - } else { + } else if (acc->reg_last_code) { info->status = acc->reg_last_code; info->status_text = *pjsip_get_status_text(acc->reg_last_code); + } else { + info->status = 0; + info->status_text = pj_str("In Progress"); } if (acc->regc) { @@ -129,14 +137,57 @@ PJ_DEF(pj_status_t) pjsua_acc_get_info( unsigned acc_index, } +PJ_DEF(pj_status_t) pjsua_acc_enum_info( pjsua_acc_info info[], + unsigned *count ) +{ + unsigned i, c; + + for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + if (!pjsua.acc[i].valid) + continue; + + pjsua_acc_get_info(i, &info[c]); + ++c; + } + + *count = c; + return PJ_SUCCESS; +} + + +/** + * Enum accounts id. + */ +PJ_DEF(pj_status_t) pjsua_acc_enum_id( pjsua_acc_id ids[], + unsigned *count ) +{ + unsigned i, c; + + for (i=0, c=0; c<*count && i<PJ_ARRAY_SIZE(pjsua.acc); ++i) { + if (!pjsua.acc[i].valid) + continue; + ids[c] = i; + ++c; + } + + *count = c; + return PJ_SUCCESS; +} + + /* * Update registration. If renew is false, then unregistration will be performed. */ -PJ_DECL(pj_status_t) pjsua_acc_set_registration(unsigned acc_index, pj_bool_t renew) +PJ_DECL(pj_status_t) pjsua_acc_set_registration(pjsua_acc_id acc_index, + pj_bool_t renew) { pj_status_t status = 0; pjsip_tx_data *tdata = 0; + PJ_ASSERT_RETURN(acc_index < (int)pjsua.config.acc_cnt, + PJ_EINVAL); + PJ_ASSERT_RETURN(pjsua.acc[acc_index].valid, PJ_EINVALIDOP); + if (renew) { if (pjsua.acc[acc_index].regc == NULL) { status = pjsua_regc_init(acc_index); diff --git a/pjsip/src/pjsua-lib/pjsua_settings.c b/pjsip/src/pjsua-lib/pjsua_settings.c index be615359..db30312d 100644 --- a/pjsip/src/pjsua-lib/pjsua_settings.c +++ b/pjsip/src/pjsua-lib/pjsua_settings.c @@ -211,7 +211,8 @@ static int my_atoi(const char *cs) /* Parse arguments. */ PJ_DEF(pj_status_t) pjsua_parse_args(int argc, char *argv[], - pjsua_config *cfg) + pjsua_config *cfg, + pj_str_t *uri_to_call) { int c; int option_index; @@ -595,22 +596,29 @@ PJ_DEF(pj_status_t) pjsua_parse_args(int argc, char *argv[], } if (pj_optind != argc) { + pj_str_t uri_arg; if (pjsua_verify_sip_url(argv[pj_optind]) != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, "Invalid SIP URI %s", argv[pj_optind])); return -1; } - cfg->uri_to_call = pj_str(argv[pj_optind]); + uri_arg = pj_str(argv[pj_optind]); + if (uri_to_call) + *uri_to_call = uri_arg; pj_optind++; /* Add URI to call to buddy list if it's not already there */ for (i=0; i<cfg->buddy_cnt; ++i) { - if (pj_stricmp(&cfg->buddy_uri[i], &cfg->uri_to_call)==0) + if (pj_stricmp(&cfg->buddy_uri[i], &uri_arg)==0) break; } if (i == cfg->buddy_cnt && cfg->buddy_cnt < PJSUA_MAX_BUDDIES) { - cfg->buddy_uri[cfg->buddy_cnt++] = cfg->uri_to_call; + cfg->buddy_uri[cfg->buddy_cnt++] = uri_arg; } + + } else { + if (uri_to_call) + uri_to_call->slen = 0; } if (pj_optind != argc) { @@ -869,7 +877,7 @@ static void dump_media_session(const char *indent, } } -PJ_DEF(void) pjsua_dump_call(int call_index, int with_media, +PJ_DEF(void) pjsua_call_dump(int call_index, int with_media, char *buffer, unsigned maxlen, const char *indent) { @@ -975,7 +983,7 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) for (i=0; i<pjsua.config.max_calls; ++i) { if (pjsua.calls[i].inv) { - pjsua_dump_call(i, detail, buf, sizeof(buf), " "); + pjsua_call_dump(i, detail, buf, sizeof(buf), " "); PJ_LOG(3,(THIS_FILE, "%s", buf)); } } @@ -993,13 +1001,14 @@ PJ_DEF(void) pjsua_dump(pj_bool_t detail) * Load settings. */ PJ_DECL(pj_status_t) pjsua_load_settings(const char *filename, - pjsua_config *cfg) + pjsua_config *cfg, + pj_str_t *uri_to_call) { int argc = 3; char *argv[4] = { "pjsua", "--config-file", NULL, NULL}; argv[2] = (char*)filename; - return pjsua_parse_args(argc, argv, cfg); + return pjsua_parse_args(argc, argv, cfg, uri_to_call); } @@ -1297,8 +1306,130 @@ PJ_DEF(pj_status_t) pjsua_save_settings(const char *filename, /** * Get pjsua running config. */ -PJ_DEF(const pjsua_config*) pjsua_get_config(void) +PJ_DEF(void) pjsua_get_config(pj_pool_t *pool, + pjsua_config *cfg) { - return &pjsua.config; + unsigned i; + + pjsua_copy_config(pool, cfg, &pjsua.config); + + /* Compact buddy uris. */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.config.buddy_uri)-1; ++i) { + if (pjsua.config.buddy_uri[i].slen == 0) { + unsigned j; + + for (j=i+1; j<PJ_ARRAY_SIZE(pjsua.config.buddy_uri); ++j) { + if (pjsua.config.buddy_uri[j].slen != 0) + break; + } + + if (j == PJ_ARRAY_SIZE(pjsua.config.buddy_uri)) + break; + else + pjsua.config.buddy_uri[i] = pjsua.config.buddy_uri[j]; + } + } + + /* Compact accounts. */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.config.acc_config)-1; ++i) { + + if (pjsua.acc[i].valid == PJ_FALSE || pjsua.acc[i].auto_gen) { + unsigned j; + + for (j=i+1; j<PJ_ARRAY_SIZE(pjsua.config.acc_config); ++j) { + if (pjsua.acc[j].valid && !pjsua.acc[j].auto_gen) + break; + } + + if (j == PJ_ARRAY_SIZE(pjsua.config.acc_config)) { + break; + } else { + pj_memcpy(&pjsua.config.acc_config[i] , + &pjsua.config.acc_config[j], + sizeof(pjsua_acc_config)); + } + } + + } + + /* Remove auto generated account from config */ + for (i=0; i<PJ_ARRAY_SIZE(pjsua.config.acc_config); ++i) { + if (pjsua.acc[i].auto_gen) + --cfg->acc_cnt; + } } + + +/***************************************************************************** + * This is a very simple PJSIP module, whose sole purpose is to display + * incoming and outgoing messages to log. This module will have priority + * higher than transport layer, which means: + * + * - incoming messages will come to this module first before reaching + * transaction layer. + * + * - outgoing messages will come to this module last, after the message + * has been 'printed' to contiguous buffer by transport layer and + * appropriate transport instance has been decided for this message. + * + */ + +/* Notification on incoming messages */ +static pj_bool_t logging_on_rx_msg(pjsip_rx_data *rdata) +{ + PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n" + "%s\n" + "--end msg--", + rdata->msg_info.len, + pjsip_rx_data_get_info(rdata), + rdata->pkt_info.src_name, + rdata->pkt_info.src_port, + rdata->msg_info.msg_buf)); + + /* Always return false, otherwise messages will not get processed! */ + return PJ_FALSE; +} + +/* Notification on outgoing messages */ +static pj_status_t logging_on_tx_msg(pjsip_tx_data *tdata) +{ + + /* Important note: + * tp_info field is only valid after outgoing messages has passed + * transport layer. So don't try to access tp_info when the module + * has lower priority than transport layer. + */ + + PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n" + "%s\n" + "--end msg--", + (tdata->buf.cur - tdata->buf.start), + pjsip_tx_data_get_info(tdata), + tdata->tp_info.dst_name, + tdata->tp_info.dst_port, + tdata->buf.start)); + + /* Always return success, otherwise message will not get sent! */ + return PJ_SUCCESS; +} + +/* The module instance. */ +pjsip_module pjsua_msg_logger = +{ + NULL, NULL, /* prev, next. */ + { "mod-pjsua-log", 13 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + &logging_on_rx_msg, /* on_rx_request() */ + &logging_on_rx_msg, /* on_rx_response() */ + &logging_on_tx_msg, /* on_tx_request. */ + &logging_on_tx_msg, /* on_tx_response() */ + NULL, /* on_tsx_state() */ + +}; + |