diff options
-rw-r--r-- | pjlib/include/pj/config.h | 4 | ||||
-rw-r--r-- | pjlib/include/pj/log.h | 1 | ||||
-rw-r--r-- | pjlib/src/pj/config.c | 2 | ||||
-rw-r--r-- | pjlib/src/pj/log.c | 9 | ||||
-rw-r--r-- | pjsip/build/pjsip_core.dsp | 1 | ||||
-rw-r--r-- | pjsip/build/pjsip_ua.dsp | 6 | ||||
-rw-r--r-- | pjsip/build/pjsua.dsp | 23 | ||||
-rw-r--r-- | pjsip/include/pjsip-ua/sip_inv.h | 1 | ||||
-rw-r--r-- | pjsip/include/pjsip-ua/sip_regc.h | 38 | ||||
-rw-r--r-- | pjsip/include/pjsip/sip_dialog.h | 1 | ||||
-rw-r--r-- | pjsip/include/pjsip_ua.h | 2 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_inv.c | 75 | ||||
-rw-r--r-- | pjsip/src/pjsip-ua/sip_reg.c | 213 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_auth_client.c | 18 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_dialog.c | 69 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_endpoint.c | 7 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_transaction.c | 63 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_ua_layer.c | 16 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util.c | 12 | ||||
-rw-r--r-- | pjsip/src/pjsip/sip_util_statefull.c | 78 | ||||
-rw-r--r-- | pjsip/src/pjsua/main.c | 494 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua.c | 216 | ||||
-rw-r--r-- | pjsip/src/pjsua/pjsua.h | 66 |
23 files changed, 1160 insertions, 255 deletions
diff --git a/pjlib/include/pj/config.h b/pjlib/include/pj/config.h index 205df1da..b836b53b 100644 --- a/pjlib/include/pj/config.h +++ b/pjlib/include/pj/config.h @@ -175,10 +175,10 @@ * This may affect the stack usage, depending whether PJ_LOG_USE_STACK_BUFFER * flag is set. * - * Default: 800 + * Default: 1500 */ #ifndef PJ_LOG_MAX_SIZE -# define PJ_LOG_MAX_SIZE 800 +# define PJ_LOG_MAX_SIZE 1500 #endif /** diff --git a/pjlib/include/pj/log.h b/pjlib/include/pj/log.h index 1cdf8577..2c706f9c 100644 --- a/pjlib/include/pj/log.h +++ b/pjlib/include/pj/log.h @@ -112,6 +112,7 @@ typedef void pj_log_func(int level, const char *data, int len); /** * Default logging writer function used by front end logger function. + * This function will print the log message to stdout only. * Application normally should NOT need to call this function, but * rather use the PJ_LOG macro. * diff --git a/pjlib/src/pj/config.c b/pjlib/src/pj/config.c index 8d14a114..40865684 100644 --- a/pjlib/src/pj/config.c +++ b/pjlib/src/pj/config.c @@ -21,7 +21,7 @@ #include <pj/ioqueue.h> static const char *id = "config.c"; -const char *PJ_VERSION = "0.5"; +const char *PJ_VERSION = "0.5.1"; PJ_DEF(void) pj_dump_config(void) { diff --git a/pjlib/src/pj/log.c b/pjlib/src/pj/log.c index 442ee942..066e765f 100644 --- a/pjlib/src/pj/log.c +++ b/pjlib/src/pj/log.c @@ -73,7 +73,7 @@ PJ_DEF(void) pj_log( const char *sender, int level, #if PJ_LOG_USE_STACK_BUFFER char log_buffer[PJ_LOG_MAX_SIZE]; #endif - int len; + int len, print_len; PJ_CHECK_STACK(); @@ -137,7 +137,12 @@ PJ_DEF(void) pj_log( const char *sender, int level, len = pre - log_buffer; /* Print the whole message to the string log_buffer. */ - len = len + vsnprintf(pre, sizeof(log_buffer)-len, format, marker); + print_len = vsnprintf(pre, sizeof(log_buffer)-len, format, marker); + if (print_len < 0) { + print_len = pj_snprintf(pre, sizeof(log_buffer)-len, + "<logging error: msg too long>"); + } + len = len + print_len; if (len > 0 && len < sizeof(log_buffer)-2) { if (log_decor & PJ_LOG_HAS_CR) { log_buffer[len++] = '\r'; diff --git a/pjsip/build/pjsip_core.dsp b/pjsip/build/pjsip_core.dsp index 30299861..08fa49d0 100644 --- a/pjsip/build/pjsip_core.dsp +++ b/pjsip/build/pjsip_core.dsp @@ -179,7 +179,6 @@ SOURCE=..\src\pjsip\sip_transaction.c # Begin Source File
SOURCE=..\src\pjsip\sip_util_statefull.c
-# PROP Exclude_From_Build 1
# End Source File
# End Group
# Begin Group "UA Layer (.c)"
diff --git a/pjsip/build/pjsip_ua.dsp b/pjsip/build/pjsip_ua.dsp index 669a805c..d2edb14c 100644 --- a/pjsip/build/pjsip_ua.dsp +++ b/pjsip/build/pjsip_ua.dsp @@ -97,8 +97,6 @@ SOURCE="..\src\pjsip-ua\sip_reg.c" !ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug"
-# PROP Exclude_From_Build 1
-
!ENDIF
# End Source File
@@ -108,6 +106,10 @@ SOURCE="..\src\pjsip-ua\sip_reg.c" # PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
+SOURCE=..\include\pjsip_ua.h
+# End Source File
+# Begin Source File
+
SOURCE="..\include\pjsip-ua\sip_inv.h"
# End Source File
# Begin Source File
diff --git a/pjsip/build/pjsua.dsp b/pjsip/build/pjsua.dsp index 7b79266f..1835b000 100644 --- a/pjsip/build/pjsua.dsp +++ b/pjsip/build/pjsua.dsp @@ -100,6 +100,17 @@ SOURCE=..\src\pjsua\main.c !ELSEIF "$(CFG)" == "pjsua - Win32 Debug"
+!ENDIF
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\main_old.c
+
+!IF "$(CFG)" == "pjsua - Win32 Release"
+
+!ELSEIF "$(CFG)" == "pjsua - Win32 Debug"
+
# PROP Exclude_From_Build 1
!ENDIF
@@ -110,6 +121,14 @@ SOURCE=..\src\pjsua\main.c SOURCE=..\src\pjsua\misc.c
# PROP Exclude_From_Build 1
# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua.c
+# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua_reg.c
+# End Source File
# End Group
# Begin Group "Header Files"
@@ -118,6 +137,10 @@ SOURCE=..\src\pjsua\misc.c SOURCE=..\src\pjsua\getopt.h
# End Source File
+# Begin Source File
+
+SOURCE=..\src\pjsua\pjsua.h
+# End Source File
# End Group
# Begin Group "Resource Files"
diff --git a/pjsip/include/pjsip-ua/sip_inv.h b/pjsip/include/pjsip-ua/sip_inv.h index 26a38972..dbd2236f 100644 --- a/pjsip/include/pjsip-ua/sip_inv.h +++ b/pjsip/include/pjsip-ua/sip_inv.h @@ -38,7 +38,6 @@ enum pjsip_inv_state PJSIP_INV_STATE_CONNECTING, /**< After 2xx is sent/received. */ PJSIP_INV_STATE_CONFIRMED, /**< After ACK is sent/received. */ PJSIP_INV_STATE_DISCONNECTED, /**< Session is terminated. */ - PJSIP_INV_STATE_TERMINATED, /**< Session will be destroyed soon. */ }; /** diff --git a/pjsip/include/pjsip-ua/sip_regc.h b/pjsip/include/pjsip-ua/sip_regc.h index 5a4e2ebe..99a08a48 100644 --- a/pjsip/include/pjsip-ua/sip_regc.h +++ b/pjsip/include/pjsip-ua/sip_regc.h @@ -26,7 +26,7 @@ #include <pjsip/sip_types.h> #include <pjsip/sip_auth.h> -#include <pjsip_mod_ua/sip_ua.h> +//#include <pjsip/sip_ua.h> PJ_BEGIN_DECL @@ -85,11 +85,13 @@ PJ_DECL(pjsip_module*) pjsip_regc_get_module(void); * @param endpt Endpoint, used to allocate pool from. * @param token A data to be associated with the client registration struct. * @param cb Pointer to callback function to receive registration status. + * @param p_regc Pointer to receive client registration structure. * - * @return client registration structure. + * @return PJ_SUCCESS on success. */ -PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb); +PJ_DECL(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb, + pjsip_regc **p_regc); /** @@ -98,8 +100,10 @@ PJ_DECL(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, * has been received, and in this case, the callback won't be called. * * @param regc The client registration structure. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(void) pjsip_regc_destroy(pjsip_regc *regc); +PJ_DECL(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc); /** * Get the memory pool associated with a registration client handle. @@ -137,11 +141,11 @@ PJ_DECL(pj_status_t) pjsip_regc_init(pjsip_regc *regc, /** * Set authentication credentials to use by this registration. * - * @param dlg The registration structure. - * @param count Number of credentials in the array. - * @param cred Array of credentials. + * @param dlg The registration structure. + * @param count Number of credentials in the array. + * @param cred Array of credentials. * - * @return Zero on success. + * @return PJ_SUCCESS on success. */ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, int count, @@ -157,20 +161,24 @@ PJ_DECL(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, * @param regc The client registration structure. * @param autoreg If non zero, the library will automatically refresh the * next registration until application unregister. + * @param p_tdata Pointer to receive the REGISTER request. * - * @return SIP REGISTER request. + * @return PJ_SUCCESS on success. */ -PJ_DECL(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg); +PJ_DECL(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, + pjsip_tx_data **p_tdata); /** * Create REGISTER request to unregister all contacts from server records. * * @param regc The client registration structure. + * @param p_tdata Pointer to receive the REGISTER request. * - * @return SIP REGISTER request. + * @return PJ_SUCCESS on success. */ -PJ_DECL(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc); +PJ_DECL(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, + pjsip_tx_data **p_tdata); /** * Update Contact details in the client registration structure. @@ -201,8 +209,10 @@ PJ_DECL(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, * * @param regc The client registration structure. * @param tdata Transmit data. + * + * @return PJ_SUCCESS on success. */ -PJ_DECL(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); +PJ_DECL(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata); PJ_END_DECL diff --git a/pjsip/include/pjsip/sip_dialog.h b/pjsip/include/pjsip/sip_dialog.h index 6a5418b8..e63ba6d1 100644 --- a/pjsip/include/pjsip/sip_dialog.h +++ b/pjsip/include/pjsip/sip_dialog.h @@ -67,6 +67,7 @@ struct pjsip_dialog void *dlg_set; /* Dialog's session properties. */ + pj_bool_t established;/**< Dialog is established? */ pjsip_uri *target; /**< Current target. */ pjsip_dlg_party local; /**< Local party info. */ pjsip_dlg_party remote; /**< Remote party info. */ diff --git a/pjsip/include/pjsip_ua.h b/pjsip/include/pjsip_ua.h index c32ada4f..f6fbdad0 100644 --- a/pjsip/include/pjsip_ua.h +++ b/pjsip/include/pjsip_ua.h @@ -20,6 +20,8 @@ #define __PJSIP_UA_H__ #include <pjsip-ua/sip_inv.h> +#include <pjsip-ua/sip_regc.h> + #endif /* __PJSIP_UA_H__ */ diff --git a/pjsip/src/pjsip-ua/sip_inv.c b/pjsip/src/pjsip-ua/sip_inv.c index f9eac982..9f63845a 100644 --- a/pjsip/src/pjsip-ua/sip_inv.c +++ b/pjsip/src/pjsip-ua/sip_inv.c @@ -106,11 +106,26 @@ static pj_status_t mod_inv_unload(void) static pj_bool_t mod_inv_on_rx_request(pjsip_rx_data *rdata) { + pjsip_dialog *dlg; + /* Ignore requests outside dialog */ - if (pjsip_rdata_get_dlg(rdata) == NULL) + dlg = pjsip_rdata_get_dlg(rdata); + if (dlg == NULL) return PJ_FALSE; - /* Ignore all. */ + /* Answer BYE with 200/OK. */ + if (rdata->msg_info.msg->line.req.method.id == PJSIP_BYE_METHOD) { + pj_status_t status; + pjsip_tx_data *tdata; + + status = pjsip_dlg_create_response(dlg, rdata, 200, NULL, &tdata); + if (status == PJ_SUCCESS) + status = pjsip_dlg_send_response(dlg, pjsip_rdata_get_tsx(rdata), + tdata); + + return status==PJ_SUCCESS ? PJ_TRUE : PJ_FALSE; + } + return PJ_FALSE; } @@ -914,7 +929,6 @@ PJ_DEF(pj_status_t) pjsip_inv_end_session( pjsip_inv_session *inv, break; case PJSIP_INV_STATE_DISCONNECTED: - case PJSIP_INV_STATE_TERMINATED: /* No need to do anything. */ PJ_TODO(RETURN_A_PROPER_STATUS_CODE_HERE); return PJ_EINVALIDOP; @@ -1066,10 +1080,11 @@ static void inv_on_state_calling( pjsip_inv_session *s, pjsip_event *e) { pjsip_transaction *tsx = e->body.tsx_state.tsx; pjsip_dialog *dlg = pjsip_tsx_get_dlg(tsx); + pj_status_t status; PJ_ASSERT_ON_FAIL(tsx && dlg, return); - if (tsx->method.id == PJSIP_INVITE_METHOD) { + if (tsx == s->invite_tsx) { switch (tsx->state) { @@ -1092,8 +1107,39 @@ static void inv_on_state_calling( pjsip_inv_session *s, pjsip_event *e) pj_assert(0); inv_set_state(s, PJSIP_INV_STATE_CONNECTING, e); + } else if (tsx->status_code==401 || tsx->status_code==407) { + + /* Handle authentication failure: + * Resend the request with Authorization header. + */ + pjsip_tx_data *tdata; + + status = pjsip_auth_clt_reinit_req(&s->dlg->auth_sess, + e->body.tsx_state.src.rdata, + tsx->last_tx, + &tdata); + + if (status != PJ_SUCCESS) { + + /* Does not have proper credentials. + * End the session. + */ + inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); + + } else { + + /* Restart session. */ + s->state = PJSIP_INV_STATE_NULL; + s->invite_tsx = NULL; + + /* Send the request. */ + status = pjsip_inv_send_msg(s, tdata, NULL ); + } + } else { + inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); + } break; @@ -1115,7 +1161,6 @@ static void inv_on_state_calling( pjsip_inv_session *s, pjsip_event *e) } else { inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); - inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e); } break; @@ -1135,7 +1180,7 @@ static void inv_on_state_incoming( pjsip_inv_session *s, pjsip_event *e) PJ_ASSERT_ON_FAIL(tsx && dlg, return); - if (tsx->method.id == PJSIP_INVITE_METHOD) { + if (tsx == s->invite_tsx) { switch (tsx->state) { case PJSIP_TSX_STATE_PROCEEDING: if (tsx->status_code > 100) @@ -1150,7 +1195,6 @@ static void inv_on_state_incoming( pjsip_inv_session *s, pjsip_event *e) case PJSIP_TSX_STATE_TERMINATED: /* This happens on transport error */ inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); - inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e); break; default: pj_assert(!"Unexpected state"); @@ -1167,7 +1211,7 @@ static void inv_on_state_early( pjsip_inv_session *s, pjsip_event *e) PJ_ASSERT_ON_FAIL(tsx && dlg, return); - if (tsx->method.id == PJSIP_INVITE_METHOD) { + if (tsx == s->invite_tsx) { switch (tsx->state) { @@ -1206,7 +1250,6 @@ static void inv_on_state_early( pjsip_inv_session *s, pjsip_event *e) } else { inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); - inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e); } break; @@ -1243,6 +1286,12 @@ static void inv_on_state_early( pjsip_inv_session *s, pjsip_event *e) } } + } else if (tsx->method.id == PJSIP_INVITE_METHOD) { + + /* Ignore previously failed INVITE transaction event + * (e.g. when rejected with 401/407) + */ + } else { pj_assert(!"Unexpected transaction type"); } @@ -1255,7 +1304,7 @@ static void inv_on_state_connecting( pjsip_inv_session *s, pjsip_event *e) PJ_ASSERT_ON_FAIL(tsx && dlg, return); - if (tsx->method.id == PJSIP_INVITE_METHOD) { + if (tsx == s->invite_tsx) { switch (tsx->state) { @@ -1269,7 +1318,6 @@ static void inv_on_state_connecting( pjsip_inv_session *s, pjsip_event *e) */ if (tsx->status_code/100 != 2) { inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); - inv_set_state(s, PJSIP_INV_STATE_TERMINATED, e); } break; @@ -1296,7 +1344,7 @@ static void inv_on_state_confirmed( pjsip_inv_session *s, pjsip_event *e) if (tsx->method.id == PJSIP_BYE_METHOD) { inv_set_state(s, PJSIP_INV_STATE_DISCONNECTED, e); - } else if (tsx->method.id == PJSIP_INVITE_METHOD) { + } else if (tsx == s->invite_tsx) { switch (tsx->state) { case PJSIP_TSX_STATE_TERMINATED: @@ -1307,6 +1355,9 @@ static void inv_on_state_confirmed( pjsip_inv_session *s, pjsip_event *e) break; } + } else if (tsx->method.id == PJSIP_INVITE_METHOD) { + + /* Re-INVITE */ } else { pj_assert(!"Unexpected transaction type"); diff --git a/pjsip/src/pjsip-ua/sip_reg.c b/pjsip/src/pjsip-ua/sip_reg.c index 1b22170e..f3a03543 100644 --- a/pjsip/src/pjsip-ua/sip_reg.c +++ b/pjsip/src/pjsip-ua/sip_reg.c @@ -16,7 +16,7 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ -#include <pjsip_mod_ua/sip_reg.h> +#include <pjsip-ua/sip_regc.h> #include <pjsip/sip_endpoint.h> #include <pjsip/sip_parser.h> #include <pjsip/sip_module.h> @@ -24,10 +24,13 @@ #include <pjsip/sip_event.h> #include <pjsip/sip_util.h> #include <pjsip/sip_auth_msg.h> +#include <pjsip/sip_errno.h> #include <pj/pool.h> #include <pj/string.h> #include <pj/guid.h> #include <pj/log.h> +#include <pj/assert.h> + #define REFRESH_TIMER 1 #define DELAY_BEFORE_REFRESH 5 @@ -41,12 +44,12 @@ struct pjsip_regc pj_pool_t *pool; pjsip_endpoint *endpt; pj_bool_t _delete_flag; - int pending_tsx; + int pending_tsx; void *token; pjsip_regc_cb *cb; - pj_str_t str_srv_url; + pj_str_t str_srv_url; pjsip_uri *srv_url; pjsip_cid_hdr *cid_hdr; pjsip_cseq_hdr *cseq_hdr; @@ -59,12 +62,8 @@ struct pjsip_regc pjsip_expires_hdr *unreg_expires_hdr; pj_uint32_t expires; - /* Credentials. */ - int cred_count; - pjsip_cred_info *cred_info; - /* Authorization sessions. */ - pjsip_auth_session auth_sess_list; + pjsip_auth_clt_sess auth_sess; /* Auto refresh registration. */ pj_bool_t auto_reg; @@ -73,17 +72,21 @@ struct pjsip_regc -PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, - pjsip_regc_cb *cb) +PJ_DEF(pj_status_t) pjsip_regc_create( pjsip_endpoint *endpt, void *token, + pjsip_regc_cb *cb, + pjsip_regc **p_regc) { pj_pool_t *pool; pjsip_regc *regc; + pj_status_t status; - if (cb == NULL) - return NULL; + /* Verify arguments. */ + PJ_ASSERT_RETURN(endpt && cb && p_regc, PJ_EINVAL); pool = pjsip_endpt_create_pool(endpt, "regc%p", 1024, 1024); - regc = pj_pool_calloc(pool, 1, sizeof(struct pjsip_regc)); + PJ_ASSERT_RETURN(pool != NULL, PJ_ENOMEM); + + regc = pj_pool_zalloc(pool, sizeof(struct pjsip_regc)); regc->pool = pool; regc->endpt = endpt; @@ -92,20 +95,26 @@ PJ_DEF(pjsip_regc*) pjsip_regc_create( pjsip_endpoint *endpt, void *token, regc->contact_buf = pj_pool_alloc(pool, PJSIP_REGC_CONTACT_BUF_SIZE); regc->expires = PJSIP_REGC_EXPIRATION_NOT_SPECIFIED; - pj_list_init(®c->auth_sess_list); + status = pjsip_auth_clt_init(®c->auth_sess, endpt, regc->pool, 0); + if (status != PJ_SUCCESS) + return status; - return regc; + /* Done */ + *p_regc = regc; + return PJ_SUCCESS; } -PJ_DEF(void) pjsip_regc_destroy(pjsip_regc *regc) +PJ_DEF(pj_status_t) pjsip_regc_destroy(pjsip_regc *regc) { if (regc->pending_tsx) { regc->_delete_flag = 1; regc->cb = NULL; } else { - pjsip_endpt_destroy_pool(regc->endpt, regc->pool); + pjsip_endpt_release_pool(regc->endpt, regc->pool); } + + return PJ_SUCCESS; } @@ -117,8 +126,7 @@ PJ_DEF(pj_pool_t*) pjsip_regc_get_pool(pjsip_regc *regc) static void set_expires( pjsip_regc *regc, pj_uint32_t expires) { if (expires != regc->expires) { - regc->expires_hdr = pjsip_expires_hdr_create(regc->pool); - regc->expires_hdr->ivalue = expires; + regc->expires_hdr = pjsip_expires_hdr_create(regc->pool, expires); } else { regc->expires_hdr = NULL; } @@ -136,7 +144,7 @@ static pj_status_t set_contact( pjsip_regc *regc, /* Concatenate contacts. */ for (i=0, s=regc->contact_buf; i<contact_cnt; ++i) { if ((s-regc->contact_buf) + contact[i].slen + 2 > PJSIP_REGC_CONTACT_BUF_SIZE) { - return -1; + return PJSIP_EURITOOLONG; } pj_memcpy(s, contact[i].ptr, contact[i].slen); s += contact[i].slen; @@ -148,11 +156,13 @@ static pj_status_t set_contact( pjsip_regc *regc, } /* Set "Contact" header. */ - regc->contact_hdr = pjsip_generic_string_hdr_create( regc->pool, &contact_STR); + regc->contact_hdr = pjsip_generic_string_hdr_create(regc->pool, + &contact_STR, + NULL); regc->contact_hdr->hvalue.ptr = regc->contact_buf; regc->contact_hdr->hvalue.slen = (s - regc->contact_buf); - return 0; + return PJ_SUCCESS; } @@ -165,6 +175,10 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, pj_uint32_t expires) { pj_str_t tmp; + pj_status_t status; + + PJ_ASSERT_RETURN(regc && srv_url && from_url && to_url && + contact_cnt && contact && expires, PJ_EINVAL); /* Copy server URL. */ pj_strdup_with_null(regc->pool, ®c->str_srv_url, srv_url); @@ -173,7 +187,7 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, tmp = regc->str_srv_url; regc->srv_url = pjsip_parse_uri( regc->pool, tmp.ptr, tmp.slen, 0); if (regc->srv_url == NULL) { - return -1; + return PJSIP_EINVALIDURI; } /* Set "From" header. */ @@ -182,8 +196,9 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, regc->from_hdr->uri = pjsip_parse_uri(regc->pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR); if (!regc->from_hdr->uri) { - PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", from_url->slen, from_url->ptr)); - return -1; + PJ_LOG(4,(THIS_FILE, "regc: invalid source URI %.*s", + from_url->slen, from_url->ptr)); + return PJSIP_EINVALIDURI; } /* Set "To" header. */ @@ -193,13 +208,14 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, PJSIP_PARSE_URI_AS_NAMEADDR); if (!regc->to_hdr->uri) { PJ_LOG(4,(THIS_FILE, "regc: invalid target URI %.*s", to_url->slen, to_url->ptr)); - return -1; + return PJSIP_EINVALIDURI; } /* Set "Contact" header. */ - if (set_contact( regc, contact_cnt, contact) != 0) - return -1; + status = set_contact( regc, contact_cnt, contact); + if (status != PJ_SUCCESS) + return status; /* Set "Expires" header, if required. */ set_expires( regc, expires); @@ -218,70 +234,63 @@ PJ_DEF(pj_status_t) pjsip_regc_init( pjsip_regc *regc, regc->unreg_contact_hdr->star = 1; /* Create "Expires" header used in unregistration. */ - regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool); - regc->unreg_expires_hdr->ivalue = 0; + regc->unreg_expires_hdr = pjsip_expires_hdr_create( regc->pool, 0); /* Done. */ - return 0; + return PJ_SUCCESS; } PJ_DEF(pj_status_t) pjsip_regc_set_credentials( pjsip_regc *regc, int count, const pjsip_cred_info cred[] ) { - if (count > 0) { - regc->cred_info = pj_pool_alloc(regc->pool, count * sizeof(pjsip_cred_info)); - pj_memcpy(regc->cred_info, cred, count * sizeof(pjsip_cred_info)); - } - regc->cred_count = count; - return 0; + PJ_ASSERT_RETURN(regc && count && cred, PJ_EINVAL); + return pjsip_auth_clt_set_credentials(®c->auth_sess, count, cred); } -static pjsip_tx_data *create_request(pjsip_regc *regc) +static pj_status_t create_request(pjsip_regc *regc, + pjsip_tx_data **p_tdata) { + pj_status_t status; pjsip_tx_data *tdata; - pjsip_msg *msg; - /* Create transmit data. */ - tdata = pjsip_endpt_create_tdata(regc->endpt); - if (!tdata) { - return NULL; - } - - /* Create request message. */ - msg = pjsip_msg_create(tdata->pool, PJSIP_REQUEST_MSG); - tdata->msg = msg; + PJ_ASSERT_RETURN(regc && p_tdata, PJ_EINVAL); + + /* Create the request. */ + status = pjsip_endpt_create_request_from_hdr( regc->endpt, + &pjsip_register_method, + regc->srv_url, + regc->from_hdr, + regc->to_hdr, + NULL, + regc->cid_hdr, + regc->cseq_hdr->cseq, + NULL, + &tdata); + if (status != PJ_SUCCESS) + return status; - /* Initialize request line. */ - pjsip_method_set(&msg->line.req.method, PJSIP_REGISTER_METHOD); - msg->line.req.uri = regc->srv_url; - - /* Add headers. */ - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->from_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->to_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cid_hdr); - pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->cseq_hdr); - /* Add cached authorization headers. */ - pjsip_auth_init_req( regc->pool, tdata, ®c->auth_sess_list, - regc->cred_count, regc->cred_info ); + pjsip_auth_clt_init_req( ®c->auth_sess, tdata ); - /* Add reference counter to transmit data. */ - pjsip_tx_data_add_ref(tdata); - - return tdata; + /* Done. */ + *p_tdata = tdata; + return PJ_SUCCESS; } -PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) +PJ_DEF(pj_status_t) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg, + pjsip_tx_data **p_tdata) { pjsip_msg *msg; + pj_status_t status; pjsip_tx_data *tdata; - tdata = create_request(regc); - if (!tdata) - return NULL; + status = create_request(regc, &tdata); + if (status != PJ_SUCCESS) + return status; + /* Add Contact header. */ msg = tdata->msg; pjsip_msg_add_hdr(msg, (pjsip_hdr*) regc->contact_hdr); if (regc->expires_hdr) @@ -294,29 +303,34 @@ PJ_DEF(pjsip_tx_data*) pjsip_regc_register(pjsip_regc *regc, pj_bool_t autoreg) regc->auto_reg = autoreg; - return tdata; + /* Done */ + *p_tdata = tdata; + return PJ_SUCCESS; } -PJ_DEF(pjsip_tx_data*) pjsip_regc_unregister(pjsip_regc *regc) +PJ_DEF(pj_status_t) pjsip_regc_unregister(pjsip_regc *regc, + pjsip_tx_data **p_tdata) { pjsip_tx_data *tdata; pjsip_msg *msg; + pj_status_t status; if (regc->timer.id != 0) { pjsip_endpt_cancel_timer(regc->endpt, ®c->timer); regc->timer.id = 0; } - tdata = create_request(regc); - if (!tdata) - return NULL; + status = create_request(regc, &tdata); + if (status != PJ_SUCCESS) + return status; msg = tdata->msg; pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_contact_hdr); pjsip_msg_add_hdr( msg, (pjsip_hdr*)regc->unreg_expires_hdr); - return tdata; + *p_tdata = tdata; + return PJ_SUCCESS; } @@ -332,7 +346,7 @@ PJ_DEF(pj_status_t) pjsip_regc_update_expires( pjsip_regc *regc, pj_uint32_t expires ) { set_expires( regc, expires ); - return 0; + return PJ_SUCCESS; } @@ -363,23 +377,26 @@ static void regc_refresh_timer_cb( pj_timer_heap_t *timer_heap, { pjsip_regc *regc = entry->user_data; pjsip_tx_data *tdata; + pj_status_t status; - PJ_UNUSED_ARG(timer_heap) + PJ_UNUSED_ARG(timer_heap); entry->id = 0; - tdata = pjsip_regc_register(regc, 1); - if (tdata) { + status = pjsip_regc_register(regc, 1, &tdata); + if (status == PJ_SUCCESS) { pjsip_regc_send(regc, tdata); } else { - pj_str_t reason = pj_str("Unable to create txdata"); + char errmsg[PJ_ERR_MSG_SIZE]; + pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); call_callback(regc, -1, &reason, NULL, -1, 0, NULL); } } static void tsx_callback(void *token, pjsip_event *event) { + pj_status_t status; pjsip_regc *regc = token; - pjsip_transaction *tsx = event->obj.tsx; + pjsip_transaction *tsx = event->body.tsx_state.tsx; /* If registration data has been deleted by user then remove registration * data from transaction's callback, and don't call callback. @@ -390,20 +407,20 @@ static void tsx_callback(void *token, pjsip_event *event) } else if (tsx->status_code == PJSIP_SC_PROXY_AUTHENTICATION_REQUIRED || tsx->status_code == PJSIP_SC_UNAUTHORIZED) { - pjsip_rx_data *rdata = event->src.rdata; + pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; pjsip_tx_data *tdata; - tdata = pjsip_auth_reinit_req( regc->endpt, - regc->pool, ®c->auth_sess_list, - regc->cred_count, regc->cred_info, - tsx->last_tx, event->src.rdata ); + status = pjsip_auth_clt_reinit_req( ®c->auth_sess, + rdata, + tsx->last_tx, + &tdata); - if (tdata) { + if (status == PJ_SUCCESS) { --regc->pending_tsx; pjsip_regc_send(regc, tdata); return; } else { - call_callback(regc, tsx->status_code, &rdata->msg->line.status.reason, + call_callback(regc, tsx->status_code, &rdata->msg_info.msg->line.status.reason, rdata, -1, 0, NULL); --regc->pending_tsx; } @@ -419,8 +436,8 @@ static void tsx_callback(void *token, pjsip_event *event) pjsip_msg *msg; pjsip_expires_hdr *expires; - rdata = event->src.rdata; - msg = rdata->msg; + rdata = event->body.tsx_state.src.rdata; + msg = rdata->msg_info.msg; hdr = pjsip_msg_find_hdr( msg, PJSIP_H_CONTACT, NULL); while (hdr) { contact[contact_cnt++] = hdr; @@ -459,14 +476,15 @@ static void tsx_callback(void *token, pjsip_event *event) } } else { - rdata = (event->src_type==PJSIP_EVENT_RX_MSG) ? event->src.rdata : NULL; + rdata = (event->body.tsx_state.type==PJSIP_EVENT_RX_MSG) ? + event->body.tsx_state.src.rdata : NULL; } /* Call callback. */ if (expiration == 0xFFFF) expiration = -1; call_callback(regc, tsx->status_code, - (rdata ? &rdata->msg->line.status.reason + (rdata ? &rdata->msg_info.msg->line.status.reason : pjsip_get_status_text(tsx->status_code)), rdata, expiration, contact_cnt, contact); @@ -480,16 +498,16 @@ static void tsx_callback(void *token, pjsip_event *event) } } -PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) +PJ_DEF(pj_status_t) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) { - int status; + pj_status_t status; /* Make sure we don't have pending transaction. */ if (regc->pending_tsx) { pj_str_t reason = pj_str("Transaction in progress"); call_callback(regc, -1, &reason, NULL, -1, 0, NULL); pjsip_tx_data_dec_ref( tdata ); - return; + return PJ_EINVALIDOP; } /* Invalidate message buffer. */ @@ -500,12 +518,15 @@ PJ_DEF(void) pjsip_regc_send(pjsip_regc *regc, pjsip_tx_data *tdata) /* Send. */ status = pjsip_endpt_send_request(regc->endpt, tdata, -1, regc, &tsx_callback); - if (status==0) + if (status==PJ_SUCCESS) ++regc->pending_tsx; else { - pj_str_t reason = pj_str("Unable to send request."); + char errmsg[PJ_ERR_MSG_SIZE]; + pj_str_t reason = pj_strerror(status, errmsg, sizeof(errmsg)); call_callback(regc, status, &reason, NULL, -1, 0, NULL); } + + return status; } diff --git a/pjsip/src/pjsip/sip_auth_client.c b/pjsip/src/pjsip/sip_auth_client.c index 1caaa901..c730bb12 100644 --- a/pjsip/src/pjsip/sip_auth_client.c +++ b/pjsip/src/pjsip/sip_auth_client.c @@ -394,11 +394,15 @@ PJ_DEF(pj_status_t) pjsip_auth_clt_set_credentials( pjsip_auth_clt_sess *sess, int cred_cnt, const pjsip_cred_info *c) { - PJ_ASSERT_RETURN(sess && cred_cnt && c, PJ_EINVAL); + PJ_ASSERT_RETURN(sess && c, PJ_EINVAL); - sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c)); - pj_memcpy(sess->cred_info, c, cred_cnt * sizeof(*c)); - sess->cred_cnt = cred_cnt; + if (cred_cnt == 0) { + sess->cred_cnt = 0; + } else { + sess->cred_info = pj_pool_alloc(sess->pool, cred_cnt * sizeof(*c)); + pj_memcpy(sess->cred_info, c, cred_cnt * sizeof(*c)); + sess->cred_cnt = cred_cnt; + } return PJ_SUCCESS; } @@ -521,6 +525,12 @@ static pj_status_t auth_respond( pj_pool_t *req_pool, pj_list_insert_before( &cached_auth->cached_hdr, cached_hdr ); } } + +# if defined(PJSIP_AUTH_AUTO_SEND_NEXT) && PJSIP_AUTH_AUTO_SEND_NEXT!=0 + if (hdr != cached_auth->last_chal) { + cached_auth->last_chal = pjsip_hdr_clone(sess_pool, hdr); + } +# endif } # endif diff --git a/pjsip/src/pjsip/sip_dialog.c b/pjsip/src/pjsip/sip_dialog.c index b4ea16d5..c6ad91ab 100644 --- a/pjsip/src/pjsip/sip_dialog.c +++ b/pjsip/src/pjsip/sip_dialog.c @@ -697,10 +697,12 @@ static pj_status_t dlg_create_request_throw( pjsip_dialog *dlg, pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)r); } - /* Copy authorization headers. */ - status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata ); - if (status != PJ_SUCCESS) - return status; + /* Copy authorization headers, if request is not ACK or CANCEL. */ + if (method->id != PJSIP_ACK_METHOD && method->id != PJSIP_CANCEL_METHOD) { + status = pjsip_auth_clt_init_req( &dlg->auth_sess, tdata ); + if (status != PJ_SUCCESS) + return status; + } /* Done. */ *p_tdata = tdata; @@ -1132,6 +1134,7 @@ on_return: void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) { unsigned i; + int res_code; /* Lock the dialog. */ pj_mutex_lock(dlg->mutex); @@ -1139,17 +1142,66 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) /* Check that rdata already has dialog in mod_data. */ pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); - /* Update the remote tag, if none is specified yet. */ - if (dlg->remote.info->tag.slen == 0 && rdata->msg_info.to->tag.slen != 0) { + /* Update the remote tag if it is different. */ + if (pj_strcmp(&dlg->remote.info->tag, &rdata->msg_info.to->tag) != 0) { pj_strdup(dlg->pool, &dlg->remote.info->tag, &rdata->msg_info.to->tag); /* No need to update remote's tag_hval since its never used. */ } - /* Update remote target when receiving certain response messages. */ + /* Keep the response's status code */ + res_code = rdata->msg_info.msg->line.status.code; + + /* When we receive response that establishes dialog, update the route + * set and dialog target. + */ + if (!dlg->established && + pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && + (res_code > 100 && res_code < 300) && + rdata->msg_info.to->tag.slen) + { + /* RFC 3271 Section 12.1.2: + * The route set MUST be set to the list of URIs in the Record-Route + * header field from the response, taken in reverse order and + * preserving all URI parameters. If no Record-Route header field + * is present in the response, the route set MUST be set to the + * empty set. This route set, even if empty, overrides any pre-existing + * route set for future requests in this dialog. + */ + pjsip_hdr *hdr, *end_hdr; + pjsip_contact_hdr *contact; + + pj_list_init(&dlg->route_set); + + end_hdr = &rdata->msg_info.msg->hdr; + for (hdr=rdata->msg_info.msg->hdr.prev; hdr!=end_hdr; hdr=hdr->prev) { + if (hdr->type == PJSIP_H_RECORD_ROUTE) { + pjsip_route_hdr *r; + r = pjsip_hdr_clone(dlg->pool, hdr); + pjsip_routing_hdr_set_route(r); + pj_list_push_back(&dlg->route_set, r); + } + } + + /* The remote target MUST be set to the URI from the Contact header + * field of the response. + */ + contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT, + NULL); + if (contact) { + dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact); + dlg->target = dlg->remote.contact->uri; + } + + dlg->established = 1; + } + + /* Update remote target (again) when receiving 2xx response messages + * that's defined as target refresh. + */ if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && - rdata->msg_info.msg->line.status.code/100 == 2) + res_code/100 == 2) { pjsip_contact_hdr *contact; @@ -1161,6 +1213,7 @@ void pjsip_dlg_on_rx_response( pjsip_dialog *dlg, pjsip_rx_data *rdata ) } } + /* Pass to dialog usages. */ for (i=0; i<dlg->usage_cnt; ++i) { pj_bool_t processed; diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c index 53561030..b95e4024 100644 --- a/pjsip/src/pjsip/sip_endpoint.c +++ b/pjsip/src/pjsip/sip_endpoint.c @@ -103,6 +103,8 @@ void init_sip_parser(void); /* Defined in sip_tel_uri.c */ pj_status_t pjsip_tel_uri_subsys_init(void); +/* Defined in sip_util_statefull.c */ +extern pjsip_module mod_stateful_util; /* * This is the global handler for memory allocation failure, for pools that @@ -490,6 +492,11 @@ PJ_DEF(pj_status_t) pjsip_endpt_create(pj_pool_factory *pf, /* Initialize capability header list. */ pj_list_init(&endpt->cap_hdr); + /* Register mod_stateful_util module (sip_util_statefull.c) */ + status = pjsip_endpt_register_module(endpt, &mod_stateful_util); + if (status != PJ_SUCCESS) + goto on_error; + /* Done. */ *p_endpt = endpt; return status; diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c index 20d90c7b..fa025993 100644 --- a/pjsip/src/pjsip/sip_transaction.c +++ b/pjsip/src/pjsip/sip_transaction.c @@ -2195,6 +2195,13 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, } } else if (tsx->status_code >= 300 && tsx->status_code <= 699) { + + +#if 0 + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ + /* + * This is the old code; it's broken for authentication. + */ pj_time_val timeout; pj_status_t status; @@ -2243,6 +2250,62 @@ static pj_status_t tsx_on_state_proceeding_uac(pjsip_transaction *tsx, tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata ); + /* XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX */ +#endif + + /* New code, taken from 0.2.9.x branch */ + pj_time_val timeout; + pjsip_tx_data *ack_tdata = NULL; + + /* Stop timer B. */ + pjsip_endpt_cancel_timer( tsx->endpt, &tsx->timeout_timer ); + + /* Generate ACK now (for INVITE) but send it later because + * dialog need to use last_tx. + */ + if (tsx->method.id == PJSIP_INVITE_METHOD) { + pj_status_t status; + + status = pjsip_endpt_create_ack( tsx->endpt, tsx->last_tx, + event->body.rx_msg.rdata, + &ack_tdata); + if (status != PJ_SUCCESS) + return status; + } + + /* Inform TU. */ + tsx_set_state( tsx, PJSIP_TSX_STATE_COMPLETED, + PJSIP_EVENT_RX_MSG, event->body.rx_msg.rdata); + + /* Generate and send ACK for INVITE. */ + if (tsx->method.id == PJSIP_INVITE_METHOD) { + pj_status_t status; + + status = tsx_send_msg( tsx, ack_tdata); + + if (ack_tdata != tsx->last_tx) { + pjsip_tx_data_dec_ref(tsx->last_tx); + tsx->last_tx = ack_tdata; + pjsip_tx_data_add_ref(ack_tdata); + } + + if (status != PJ_SUCCESS) { + return status; + } + } + + /* Start Timer D with TD/T4 timer if unreliable transport is used. */ + if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport) == 0) { + if (tsx->method.id == PJSIP_INVITE_METHOD) { + timeout = td_timer_val; + } else { + timeout = t4_timer_val; + } + } else { + timeout.sec = timeout.msec = 0; + } + pjsip_endpt_schedule_timer( tsx->endpt, &tsx->timeout_timer, &timeout); + } else { // Shouldn't happen because there's no timer for this state. pj_assert(!"Unexpected event"); diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c index c01e78ee..2d78a3ea 100644 --- a/pjsip/src/pjsip/sip_ua_layer.c +++ b/pjsip/src/pjsip/sip_ua_layer.c @@ -617,15 +617,24 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) /* Check for forked response. * Request will fork only for the initial INVITE request. */ - if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && - rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) - { + + //This doesn't work when there is authentication challenge, since + //first_cseq evaluation will yield false. + //if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD && + // rdata->msg_info.cseq->cseq == dlg_set->dlg_list.next->local.first_cseq) + + if (rdata->msg_info.cseq->method.id == PJSIP_INVITE_METHOD) { pj_str_t *to_tag = &rdata->msg_info.to->tag; /* Must hold UA mutex before accessing dialog set. */ pj_mutex_lock(mod_ua.mutex); dlg = dlg_set->dlg_list.next; + + /* Forking handling is temporarily disabled. */ + PJ_TODO(UA_LAYER_HANDLE_FORKING); + +#if 0 while (dlg != (pjsip_dialog*)&dlg_set->dlg_list) { /* If there is dialog with no remote tag (i.e. dialog has not @@ -669,6 +678,7 @@ static pj_bool_t mod_ua_on_rx_response(pjsip_rx_data *rdata) return PJ_TRUE; } } +#endif /* Done with the dialog set. */ pj_mutex_unlock(mod_ua.mutex); diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c index ba068675..1a3fc3b6 100644 --- a/pjsip/src/pjsip/sip_util.c +++ b/pjsip/src/pjsip/sip_util.c @@ -441,10 +441,11 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, const pjsip_cid_hdr *cid_hdr; const pjsip_cseq_hdr *cseq_hdr; const pjsip_hdr *hdr; + pjsip_hdr *via; pjsip_to_hdr *to; pj_status_t status; - /* rdata must be a final response. */ + /* rdata must be a non-2xx final response. */ pj_assert(rdata->msg_info.msg->type==PJSIP_RESPONSE_MSG && rdata->msg_info.msg->line.status.code >= 300); @@ -487,11 +488,14 @@ PJ_DEF(pj_status_t) pjsip_endpt_create_ack( pjsip_endpoint *endpt, to = (pjsip_to_hdr*) pjsip_msg_find_hdr(ack->msg, PJSIP_H_TO, NULL); pj_strdup(ack->pool, &to->tag, &rdata->msg_info.to->tag); + + /* Clear Via headers in the new request. */ + while ((via=pjsip_msg_find_hdr(ack->msg, PJSIP_H_VIA, NULL)) != NULL) + pj_list_erase(via); + /* Must contain single Via, just as the original INVITE. */ hdr = pjsip_msg_find_hdr( invite_msg, PJSIP_H_VIA, NULL); - if (hdr) { - pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) ); - } + pjsip_msg_insert_first_hdr( ack->msg, pjsip_hdr_clone(ack->pool,hdr) ); /* If the original INVITE has Route headers, those header fields MUST * appear in the ACK. diff --git a/pjsip/src/pjsip/sip_util_statefull.c b/pjsip/src/pjsip/sip_util_statefull.c index 41efcddc..f9c6138f 100644 --- a/pjsip/src/pjsip/sip_util_statefull.c +++ b/pjsip/src/pjsip/sip_util_statefull.c @@ -21,50 +21,56 @@ #include <pjsip/sip_endpoint.h> #include <pjsip/sip_transaction.h> #include <pjsip/sip_event.h> +#include <pjsip/sip_errno.h> #include <pj/pool.h> +#include <pj/assert.h> -struct aux_tsx_data +struct tsx_data { void *token; - void (*cb)(void*,pjsip_event*); + void (*cb)(void*, pjsip_event*); }; -static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event ); +static void mod_util_on_tsx_state(pjsip_transaction*, pjsip_event*); -pjsip_module aux_tsx_module = +/* This module will be registered in pjsip_endpt.c */ + +pjsip_module mod_stateful_util = { - NULL, NULL, /* prev and next */ - { "Aux-Tsx", 7}, /* Name. */ - -1, /* Id */ - PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ - NULL, /* User data. */ - 0, /* Number of methods supported (=0). */ - { 0 }, /* Array of methods (none) */ - NULL, /* load() */ - NULL, /* start() */ - NULL, /* stop() */ - NULL, /* unload() */ - NULL, /* on_rx_request() */ - NULL, /* on_rx_response() */ - &aux_tsx_handler, /* tsx_handler() */ + NULL, NULL, /* prev, next. */ + { "mod-stateful-util", 17 }, /* Name. */ + -1, /* Id */ + PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */ + NULL, /* User data. */ + NULL, /* load() */ + NULL, /* start() */ + NULL, /* stop() */ + NULL, /* unload() */ + NULL, /* on_rx_request() */ + NULL, /* on_rx_response() */ + NULL, /* on_tx_request. */ + NULL, /* on_tx_response() */ + &mod_util_on_tsx_state, /* on_tsx_state() */ }; -static void aux_tsx_handler( pjsip_transaction *tsx, pjsip_event *event ) +static void mod_util_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) { - struct aux_tsx_data *tsx_data; + struct tsx_data *tsx_data; if (event->type != PJSIP_EVENT_TSX_STATE) return; - if (tsx->module_data[aux_tsx_module.id] == NULL) + + tsx_data = tsx->mod_data[mod_stateful_util.id]; + if (tsx_data == NULL) return; + if (tsx->status_code < 200) return; /* Call the callback, if any, and prevent the callback to be called again * by clearing the transaction's module_data. */ - tsx_data = tsx->module_data[aux_tsx_module.id]; - tsx->module_data[aux_tsx_module.id] = NULL; + tsx->mod_data[mod_stateful_util.id] = NULL; if (tsx_data->cb) { (*tsx_data->cb)(tsx_data->token, event); @@ -79,30 +85,24 @@ PJ_DEF(pj_status_t) pjsip_endpt_send_request( pjsip_endpoint *endpt, void (*cb)(void*,pjsip_event*)) { pjsip_transaction *tsx; - struct aux_tsx_data *tsx_data; + struct tsx_data *tsx_data; pj_status_t status; - status = pjsip_endpt_create_tsx(endpt, &tsx); - if (!tsx) { + PJ_ASSERT_RETURN(endpt && tdata && (timeout==-1 || timeout>0), PJ_EINVAL); + + status = pjsip_tsx_create_uac(&mod_stateful_util, tdata, &tsx); + if (status != PJ_SUCCESS) { pjsip_tx_data_dec_ref(tdata); - return -1; + return status; } - tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct aux_tsx_data)); + tsx_data = pj_pool_alloc(tsx->pool, sizeof(struct tsx_data)); tsx_data->token = token; tsx_data->cb = cb; - tsx->module_data[aux_tsx_module.id] = tsx_data; + tsx->mod_data[mod_stateful_util.id] = tsx_data; - if (pjsip_tsx_init_uac(tsx, tdata) != 0) { - pjsip_endpt_destroy_tsx(endpt, tsx); - pjsip_tx_data_dec_ref(tdata); - return -1; - } + PJ_TODO(IMPLEMENT_TIMEOUT_FOR_SEND_REQUEST); - pjsip_endpt_register_tsx(endpt, tsx); - pjsip_tx_data_invalidate_msg(tdata); - pjsip_tsx_on_tx_msg(tsx, tdata); - pjsip_tx_data_dec_ref(tdata); - return 0; + return pjsip_tsx_send_msg(tsx, NULL); } diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c index c921794e..f98ce0bd 100644 --- a/pjsip/src/pjsua/main.c +++ b/pjsip/src/pjsua/main.c @@ -17,6 +17,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "pjsua.h" +#include "getopt.h" + /* For debugging, disable threading. */ //#define NO_WORKER_THREAD @@ -32,7 +34,7 @@ static pjsip_inv_session *inv_session; /* * Notify UI when invite state has changed. */ -void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) +void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { const char *state_names[] = { @@ -50,9 +52,7 @@ void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state])); - if (inv->state == PJSIP_INV_STATE_DISCONNECTED || - inv->state == PJSIP_INV_STATE_TERMINATED) - { + if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { if (inv == inv_session) inv_session = NULL; @@ -103,7 +103,7 @@ static void ui_console_main(void) continue; } -#if 0 +#if 1 printf("Enter URL to call: "); fgets(buf, sizeof(buf), stdin); @@ -121,9 +121,11 @@ static void ui_console_main(void) /* Make call! : */ pjsua_invite(buf, &inv); -#endif + +#else pjsua_invite("sip:localhost:5061", &inv); +#endif break; @@ -162,6 +164,22 @@ 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" @@ -173,12 +191,20 @@ static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata) rdata->pkt_info.src_port, rdata->msg_info.msg_buf)); - /* Must return false for logger! */ + /* 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--", @@ -188,9 +214,11 @@ static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata) tdata->tp_info.dst_port, tdata->buf.start)); + /* Always return success, otherwise message will not get sent! */ return PJ_SUCCESS; } +/* The module instance. */ static pjsip_module console_msg_logger = { NULL, NULL, /* prev, next. */ @@ -211,7 +239,427 @@ static pjsip_module console_msg_logger = }; -int main() + +/***************************************************************************** + * Console application custom logging: + */ + + +static FILE *log_file; + + +static void app_log_writer(int level, const char *buffer, int len) +{ + /* Write to both stdout and file. */ + + if (level <= pjsua.app_log_level) + pj_log_write(level, buffer, len); + + if (log_file) { + fwrite(buffer, len, 1, log_file); + fflush(log_file); + } +} + + +void app_logging_init(void) +{ + /* Redirect log function to ours */ + + pj_log_set_log_func( &app_log_writer ); + + /* If output log file is desired, create the file: */ + + if (pjsua.log_filename) + log_file = fopen(pjsua.log_filename, "wt"); +} + + +void app_logging_shutdown(void) +{ + /* Close logging file, if any: */ + + if (log_file) { + fclose(log_file); + log_file = NULL; + } +} + +/***************************************************************************** + * Command line argument processing: + */ + + +/* Show usage */ +static void usage(void) +{ + puts("Usage:"); + puts(" pjsua [options] [sip-url]"); + puts(""); + puts(" [sip-url] Default URL to invite."); + puts(""); + puts("General options:"); + puts(" --config-file=file Read the config/arguments from file."); + puts(" --log-file=fname Log to filename (default stderr)"); + puts(" --log-level=N Set log max level to N (0(none) to 6(trace))"); + puts(" --app-log-level=N Set log max level for stdout display to N"); + puts(" --help Display this help screen"); + puts(" --version Display version info"); + puts(""); + puts("Media options:"); + puts(" --null-audio Use NULL audio device"); + puts(""); + puts("User Agent options:"); + puts(" --auto-answer=sec Auto-answer all incoming calls after sec seconds."); + puts(" --auto-hangup=sec Auto-hangup all calls after sec seconds."); + puts(""); + puts("SIP options:"); + puts(" --local-port=port Set TCP/UDP port"); + puts(" --id=url Set the URL of local ID (used in From header)"); + puts(" --contact=url Override the Contact information"); + puts(" --proxy=url Set the URL of proxy server"); + puts(" --outbound=url Set the URL of outbound proxy server"); + puts(" --registrar=url Set the URL of registrar server"); + puts(" --reg-timeout=secs Set registration interval to secs (default 3600)"); + puts(""); + puts("Authentication options:"); + puts(" --realm=string Set realm"); + puts(" --username=string Set authentication username"); + puts(" --password=string Set authentication password"); + puts(""); + puts("STUN options (all must be specified):"); + puts(" --use-stun1=host[:port]"); + puts(" --use-stun2=host[:port] Use STUN and set host name and port of STUN servers"); + puts(""); + puts("SIMPLE options (may be specified more than once):"); + puts(" --add-buddy url Add the specified URL to the buddy list."); + puts(" --offer-x-ms-msg Offer \"x-ms-message\" in outgoing INVITE"); + puts(" --no-presence Do not subscribe presence of buddies"); + puts(""); + fflush(stdout); +} + + +/* + * Verify that valid SIP url is given. + */ +static pj_status_t verify_sip_url(char *url) +{ + pjsip_uri *p; + pj_pool_t *pool; + int len = (url ? strlen(url) : 0); + + if (!len) return -1; + + pool = pj_pool_create(&pjsua.cp.factory, "check%p", 1024, 0, NULL); + if (!pool) return -1; + + p = pjsip_parse_uri(pool, url, len, 0); + if (!p || pj_stricmp2(pjsip_uri_get_scheme(p), "sip") != 0) + p = NULL; + + pj_pool_release(pool); + return p ? 0 : -1; +} + + +/* + * Read command arguments from config file. + */ +static int read_config_file(pj_pool_t *pool, const char *filename, + int *app_argc, char ***app_argv) +{ + int i; + FILE *fhnd; + char line[200]; + int argc = 0; + char **argv; + enum { MAX_ARGS = 64 }; + + /* Allocate MAX_ARGS+1 (argv needs to be terminated with NULL argument) */ + argv = pj_pool_calloc(pool, MAX_ARGS+1, sizeof(char*)); + argv[argc++] = *app_argv[0]; + + /* Open config file. */ + fhnd = fopen(filename, "rt"); + if (!fhnd) { + printf("Unable to open config file %s\n", filename); + return -1; + } + + /* Scan tokens in the file. */ + while (argc < MAX_ARGS && !feof(fhnd)) { + char *token, *p = line; + + if (fgets(line, sizeof(line), fhnd) == NULL) break; + + for (token = strtok(p, " \t\r\n"); argc < MAX_ARGS; + token = strtok(NULL, " \t\r\n")) + { + int token_len; + + if (!token) break; + if (*token == '#') break; + + token_len = strlen(token); + if (!token_len) + continue; + argv[argc] = pj_pool_alloc(pool, token_len+1); + pj_memcpy(argv[argc], token, token_len+1); + ++argc; + } + } + + /* Copy arguments from command line */ + for (i=1; i<*app_argc && argc < MAX_ARGS; ++i) + argv[argc++] = (*app_argv)[i]; + + if (argc == MAX_ARGS && (i!=*app_argc || !feof(fhnd))) { + printf("Too many arguments specified in cmd line/config file\n"); + fclose(fhnd); + return -1; + } + + fclose(fhnd); + + /* Assign the new command line back to the original command line. */ + *app_argc = argc; + *app_argv = argv; + return 0; + +} + + +/* Parse arguments. */ +static pj_status_t parse_args(int argc, char *argv[]) +{ + int c; + int option_index; + enum { OPT_CONFIG_FILE, OPT_LOG_FILE, OPT_LOG_LEVEL, OPT_APP_LOG_LEVEL, + OPT_HELP, OPT_VERSION, OPT_NULL_AUDIO, + OPT_LOCAL_PORT, OPT_PROXY, OPT_OUTBOUND_PROXY, OPT_REGISTRAR, + OPT_REG_TIMEOUT, OPT_ID, OPT_CONTACT, + OPT_REALM, OPT_USERNAME, OPT_PASSWORD, + OPT_USE_STUN1, OPT_USE_STUN2, + OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE, + OPT_AUTO_ANSWER, OPT_AUTO_HANGUP}; + struct option long_options[] = { + { "config-file",1, 0, OPT_CONFIG_FILE}, + { "log-file", 1, 0, OPT_LOG_FILE}, + { "log-level", 1, 0, OPT_LOG_LEVEL}, + { "app-log-level",1,0,OPT_APP_LOG_LEVEL}, + { "help", 0, 0, OPT_HELP}, + { "version", 0, 0, OPT_VERSION}, + { "null-audio", 0, 0, OPT_NULL_AUDIO}, + { "local-port", 1, 0, OPT_LOCAL_PORT}, + { "proxy", 1, 0, OPT_PROXY}, + { "outbound", 1, 0, OPT_OUTBOUND_PROXY}, + { "registrar", 1, 0, OPT_REGISTRAR}, + { "reg-timeout",1, 0, OPT_REG_TIMEOUT}, + { "id", 1, 0, OPT_ID}, + { "contact", 1, 0, OPT_CONTACT}, + { "realm", 1, 0, OPT_REALM}, + { "username", 1, 0, OPT_USERNAME}, + { "password", 1, 0, OPT_PASSWORD}, + { "use-stun1", 1, 0, OPT_USE_STUN1}, + { "use-stun2", 1, 0, OPT_USE_STUN2}, + { "add-buddy", 1, 0, OPT_ADD_BUDDY}, + { "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG}, + { "no-presence", 0, 0, OPT_NO_PRESENCE}, + { "auto-answer",1, 0, OPT_AUTO_ANSWER}, + { "auto-hangup",1, 0, OPT_AUTO_HANGUP}, + { NULL, 0, 0, 0} + }; + pj_status_t status; + char *config_file = NULL; + + /* Run getopt once to see if user specifies config file to read. */ + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + switch (c) { + case OPT_CONFIG_FILE: + config_file = optarg; + break; + } + if (config_file) + break; + } + + if (config_file) { + status = read_config_file(pjsua.pool, config_file, &argc, &argv); + if (status != 0) + return status; + } + + + /* Reinitialize and re-run getopt again, possibly with new arguments + * read from config file. + */ + optind = 0; + while ((c=getopt_long(argc, argv, "", long_options, &option_index)) != -1) { + char *p; + pj_str_t tmp; + long lval; + + switch (c) { + + case OPT_LOG_FILE: + pjsua.log_filename = optarg; + break; + + case OPT_LOG_LEVEL: + c = pj_strtoul(pj_cstr(&tmp, optarg)); + if (c < 0 || c > 6) { + printf("Error: expecting integer value 0-6 for --log-level\n"); + return PJ_EINVAL; + } + pj_log_set_level( c ); + break; + + case OPT_APP_LOG_LEVEL: + pjsua.app_log_level = pj_strtoul(pj_cstr(&tmp, optarg)); + if (pjsua.app_log_level < 0 || pjsua.app_log_level > 6) { + printf("Error: expecting integer value 0-6 for --app-log-level\n"); + return PJ_EINVAL; + } + break; + + case OPT_HELP: + usage(); + return PJ_EINVAL; + + case OPT_VERSION: /* version */ + pj_dump_config(); + return PJ_EINVAL; + + case OPT_NULL_AUDIO: + pjsua.null_audio = 1; + break; + + case OPT_LOCAL_PORT: /* local-port */ + lval = pj_strtoul(pj_cstr(&tmp, optarg)); + if (lval < 1 || lval > 65535) { + printf("Error: expecting integer value for --local-port\n"); + return PJ_EINVAL; + } + pjsua.sip_port = (pj_uint16_t)lval; + break; + + case OPT_PROXY: /* proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in proxy argument\n", optarg); + return PJ_EINVAL; + } + pjsua.proxy = pj_str(optarg); + break; + + case OPT_OUTBOUND_PROXY: /* outbound proxy */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in outbound proxy argument\n", optarg); + return PJ_EINVAL; + } + pjsua.outbound_proxy = pj_str(optarg); + break; + + case OPT_REGISTRAR: /* registrar */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in registrar argument\n", optarg); + return PJ_EINVAL; + } + pjsua.registrar_uri = pj_str(optarg); + break; + + case OPT_REG_TIMEOUT: /* reg-timeout */ + pjsua.reg_timeout = pj_strtoul(pj_cstr(&tmp,optarg)); + if (pjsua.reg_timeout < 1 || pjsua.reg_timeout > 3600) { + printf("Error: invalid value for --reg-timeout (expecting 1-3600)\n"); + return PJ_EINVAL; + } + break; + + case OPT_ID: /* id */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in local id argument\n", optarg); + return PJ_EINVAL; + } + pjsua.local_uri = pj_str(optarg); + break; + + case OPT_CONTACT: /* contact */ + if (verify_sip_url(optarg) != 0) { + printf("Error: invalid SIP URL '%s' in contact argument\n", optarg); + return PJ_EINVAL; + } + pjsua.contact_uri = pj_str(optarg); + break; + + case OPT_USERNAME: /* Default authentication user */ + if (!pjsua.cred_count) pjsua.cred_count = 1; + pjsua.cred_info[0].username = pj_str(optarg); + break; + + case OPT_REALM: /* Default authentication realm. */ + if (!pjsua.cred_count) pjsua.cred_count = 1; + pjsua.cred_info[0].realm = pj_str(optarg); + break; + + case OPT_PASSWORD: /* authentication password */ + if (!pjsua.cred_count) pjsua.cred_count = 1; + pjsua.cred_info[0].data_type = 0; + pjsua.cred_info[0].data = pj_str(optarg); + break; + + case OPT_USE_STUN1: /* STUN server 1 */ + p = pj_native_strchr(optarg, ':'); + if (p) { + *p = '\0'; + pjsua.stun_srv1 = pj_str(optarg); + pjsua.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1)); + if (pjsua.stun_port1 < 1 || pjsua.stun_port1 > 65535) { + printf("Error: expecting port number with option --use-stun1\n"); + return PJ_EINVAL; + } + } else { + pjsua.stun_port1 = 3478; + pjsua.stun_srv1 = pj_str(optarg); + } + break; + + case OPT_USE_STUN2: /* STUN server 2 */ + p = pj_native_strchr(optarg, ':'); + if (p) { + *p = '\0'; + pjsua.stun_srv2 = pj_str(optarg); + pjsua.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1)); + if (pjsua.stun_port2 < 1 || pjsua.stun_port2 > 65535) { + printf("Error: expecting port number with option --use-stun2\n"); + return PJ_EINVAL; + } + } else { + pjsua.stun_port2 = 3478; + pjsua.stun_srv2 = pj_str(optarg); + } + break; + } + } + + if (optind != argc) { + printf("Error: unknown options %s\n", argv[optind]); + return PJ_EINVAL; + } + + if (pjsua.reg_timeout == 0) + pjsua.reg_timeout = 3600; + + + return PJ_SUCCESS; +} + + + +/***************************************************************************** + * main(): + */ +int main(int argc, char *argv[]) { /* Init default settings. */ @@ -223,13 +671,24 @@ int main() #endif - /* Initialize pjsua. - * This will start worker thread, client registration, etc. + /* Initialize pjsua (to create pool etc). */ if (pjsua_init() != PJ_SUCCESS) return 1; + + /* Parse command line arguments: */ + + if (parse_args(argc, argv) != PJ_SUCCESS) + return 1; + + + /* Init logging: */ + + app_logging_init(); + + /* Register message logger to print incoming and outgoing * messages. */ @@ -237,6 +696,15 @@ int main() pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger); + /* Start pjsua! */ + + if (pjsua_start() != PJ_SUCCESS) { + + pjsua_destroy(); + return 1; + } + + /* Sleep for a while, let any messages get printed to console: */ pj_thread_sleep(500); @@ -251,6 +719,12 @@ int main() pjsua_destroy(); + + /* Close logging: */ + + app_logging_shutdown(); + + /* Exit... */ return 0; diff --git a/pjsip/src/pjsua/pjsua.c b/pjsip/src/pjsua/pjsua.c index 8e9cbde6..a5fafe66 100644 --- a/pjsip/src/pjsua/pjsua.c +++ b/pjsip/src/pjsua/pjsua.c @@ -23,8 +23,7 @@ struct pjsua pjsua; #define THIS_FILE "pjsua.c" -#define PJSUA_LOCAL_URI "<sip:bennylp@192.168.0.7>" -#define PJSUA_CONTACT_URI "<sip:bennylp@192.168.0.7>" +#define PJSUA_LOCAL_URI "<sip:user@127.0.0.1>" static char *PJSUA_DUMMY_SDP_OFFER = "v=0\r\n" @@ -71,6 +70,10 @@ void pjsua_default(void) /* Default: do not use STUN: */ pjsua.stun_port1 = pjsua.stun_port2 = 0; + + /* Default URIs: */ + + pjsua.local_uri = pj_str(PJSUA_LOCAL_URI); } @@ -129,7 +132,7 @@ static pj_bool_t mod_pjsua_on_rx_response(pjsip_rx_data *rdata) */ static void pjsua_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) { - ui_inv_on_state_changed(inv, e); + pjsua_ui_inv_on_state_changed(inv, e); } @@ -396,46 +399,7 @@ static pj_status_t init_stack(void) } - /* Add UDP transport: */ - - { - /* Init the published name for the transport. - * Depending whether STUN is used, this may be the STUN mapped - * address, or socket's bound address. - */ - pjsip_host_port addr_name; - - addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr); - addr_name.host.slen = pj_native_strlen(addr_name.host.ptr); - addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port); - - /* Create UDP transport from previously created UDP socket: */ - - status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock, - &addr_name, 1, NULL); - if (status != PJ_SUCCESS) { - pjsua_perror("Unable to start UDP transport", status); - goto on_error; - } - } - - /* Initialize local user info and contact: */ - - { - pj_strdup2(pjsua.pool, &pjsua.local_uri, PJSUA_LOCAL_URI); - pj_strdup2(pjsua.pool, &pjsua.contact_uri, PJSUA_CONTACT_URI); - } - - /* Initialize global route_set: */ - - PJ_TODO(INIT_GLOBAL_ROUTE_SET); - - - /* Start registration: */ - - PJ_TODO(START_REGISTRATION); - - /* Done? */ + /* Done */ return PJ_SUCCESS; @@ -461,11 +425,11 @@ static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg) /* * Initialize pjsua application. - * This will start the registration process, if registration is configured. + * This will initialize all libraries, create endpoint instance, and register + * pjsip modules. */ pj_status_t pjsua_init(void) { - int i; /* Must be signed */ pj_status_t status; /* Init PJLIB logging: */ @@ -491,25 +455,142 @@ pj_status_t pjsua_init(void) pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL); - /* Init sockets (STUN etc): */ + /* Init PJSIP and all the modules: */ - status = init_sockets(); + status = init_stack(); if (status != PJ_SUCCESS) { pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror("init_sockets() has returned error", status); + pjsua_perror("Stack initialization has returned error", status); return status; } + /* Done. */ + return PJ_SUCCESS; +} + - /* Init PJSIP and all the modules: */ - status = init_stack(); +/* + * Start pjsua stack. + * This will start the registration process, if registration is configured. + */ +pj_status_t pjsua_start(void) +{ + int i; /* Must be signed */ + pjsip_transport *udp_transport; + pj_status_t status; + + /* Init sockets (STUN etc): */ + + status = init_sockets(); if (status != PJ_SUCCESS) { pj_caching_pool_destroy(&pjsua.cp); - pjsua_perror("Stack initialization has returned error", status); + pjsua_perror("init_sockets() has returned error", status); return status; } + + /* Add UDP transport: */ + + { + /* Init the published name for the transport. + * Depending whether STUN is used, this may be the STUN mapped + * address, or socket's bound address. + */ + pjsip_host_port addr_name; + + addr_name.host.ptr = pj_inet_ntoa(pjsua.sip_sock_name.sin_addr); + addr_name.host.slen = pj_native_strlen(addr_name.host.ptr); + addr_name.port = pj_ntohs(pjsua.sip_sock_name.sin_port); + + /* Create UDP transport from previously created UDP socket: */ + + status = pjsip_udp_transport_attach( pjsua.endpt, pjsua.sip_sock, + &addr_name, 1, + &udp_transport); + if (status != PJ_SUCCESS) { + pjsua_perror("Unable to start UDP transport", status); + return status; + } + } + + /* Initialize Contact URI, if one is not specified: */ + + if (pjsua.contact_uri.slen == 0 && pjsua.local_uri.slen) { + + pjsip_uri *uri; + pjsip_sip_uri *sip_uri; + char contact[128]; + int len; + + /* The local Contact is the username@ip-addr, where + * - username is taken from the local URI, + * - ip-addr in UDP transport's address name (which may have been + * resolved from STUN. + */ + + /* Need to parse local_uri to get the elements: */ + + uri = pjsip_parse_uri(pjsua.pool, pjsua.local_uri.ptr, + pjsua.local_uri.slen, 0); + if (uri == NULL) { + pjsua_perror("Invalid local URI", PJSIP_EINVALIDURI); + return PJSIP_EINVALIDURI; + } + + + /* Local URI MUST be a SIP or SIPS: */ + + if (!PJSIP_URI_SCHEME_IS_SIP(uri) && !PJSIP_URI_SCHEME_IS_SIPS(uri)) { + pjsua_perror("Invalid local URI", PJSIP_EINVALIDSCHEME); + return PJSIP_EINVALIDSCHEME; + } + + + /* Get the SIP URI object: */ + + sip_uri = (pjsip_sip_uri*) pjsip_uri_get_uri(uri); + + + /* Build temporary contact string. */ + + if (sip_uri->user.slen) { + + /* With the user part. */ + len = pj_snprintf(contact, sizeof(contact), + "<sip:%.*s@%.*s:%d>", + sip_uri->user.slen, + sip_uri->user.ptr, + udp_transport->local_name.host.slen, + udp_transport->local_name.host.ptr, + udp_transport->local_name.port); + } else { + + /* Without user part */ + + len = pj_snprintf(contact, sizeof(contact), + "<sip:%.*s:%d>", + udp_transport->local_name.host.slen, + udp_transport->local_name.host.ptr, + udp_transport->local_name.port); + } + + if (len < 1 || len >= sizeof(contact)) { + pjsua_perror("Invalid Contact", PJSIP_EURITOOLONG); + return PJSIP_EURITOOLONG; + } + + /* Duplicate Contact uri. */ + + pj_strdup2(pjsua.pool, &pjsua.contact_uri, contact); + + } + + /* Initialize global route_set: */ + + PJ_TODO(INIT_GLOBAL_ROUTE_SET); + + /* Create worker thread(s), if required: */ for (i=0; i<pjsua.thread_cnt; ++i) { @@ -526,7 +607,21 @@ pj_status_t pjsua_init(void) } } - /* Done. */ + /* Start registration: */ + + /* Create client registration session: */ + + status = pjsua_regc_init(); + if (status != PJ_SUCCESS) + return status; + + /* Perform registration, if required. */ + if (pjsua.regc) { + pjsua_regc_update(1); + } + + + return PJ_SUCCESS; } @@ -538,6 +633,16 @@ pj_status_t pjsua_destroy(void) { int i; + /* Unregister, if required: */ + if (pjsua.regc) { + + pjsua_regc_update(0); + + /* Wait for some time to allow unregistration to complete: */ + + pj_thread_sleep(500); + } + /* Signal threads to quit: */ pjsua.quit_flag = 1; @@ -610,9 +715,14 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, } + /* Set dialog Route-Set: */ + + PJ_TODO(INIT_DIALOG_ROUTE_SET); + /* Set credentials: */ - PJ_TODO(SET_DIALOG_CREDENTIALS); + pjsip_auth_clt_set_credentials( &dlg->auth_sess, pjsua.cred_count, + pjsua.cred_info); /* Create initial INVITE: */ diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h index 20e300f3..9fb6a6b4 100644 --- a/pjsip/src/pjsua/pjsua.h +++ b/pjsip/src/pjsua/pjsua.h @@ -47,9 +47,29 @@ extern struct pjsua /* User info: */ + pj_str_t local_uri; /**< Uri in From: header. */ pj_str_t contact_uri; /**< Uri in Contact: header. */ + /* Proxy URLs: */ + + pj_str_t proxy; + pj_str_t outbound_proxy; + + /* Registration: */ + + pj_str_t registrar_uri; + pjsip_regc *regc; + pj_int32_t reg_timeout; + pj_timer_entry regc_timer; + + + /* Authentication credentials: */ + + int cred_count; + pjsip_cred_info cred_info[4]; + + /* Threading: */ int thread_cnt; /**< Thread count. */ @@ -76,11 +96,18 @@ extern struct pjsua int stun_port2; + /* Media stack: */ + + pj_bool_t null_audio; + pj_med_mgr_t *mmgr; + + /* Misc: */ int log_level; /**< Logging verbosity. */ int app_log_level; /**< stdout log verbosity. */ unsigned log_decor; /**< Log decoration. */ + char *log_filename; /**< Log filename. */ } pjsua; @@ -102,13 +129,28 @@ void pjsua_perror(const char *title, pj_status_t status); /** - * Initialize pjsua application. - * This will start the registration process, if registration is configured. + * Initialize pjsua application. Application can call this before parsing + * application settings. + * + * This will initialize all libraries, create endpoint instance, and register + * pjsip modules. Transport will NOT be created however. + * + * Application may register module after calling this function. */ pj_status_t pjsua_init(void); /** + * Start pjsua stack. Application calls this after pjsua settings has been + * configured. + * + * This will start the transport, worker threads (if any), and registration + * process, if registration is configured. + */ +pj_status_t pjsua_start(void); + + +/** * Destroy pjsua. */ pj_status_t pjsua_destroy(void); @@ -122,6 +164,24 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, /***************************************************************************** + * PJSUA Client Registration API. + */ + +/** + * Initialize client registration session. + * + * @param app_callback Optional callback + */ +pj_status_t pjsua_regc_init(void); + +/** + * Update registration or perform unregistration. If renew argument is zero, + * this will start unregistration process. + */ +void pjsua_regc_update(pj_bool_t renew); + + +/***************************************************************************** * User Interface API. * The UI API specifies functions that will be called by pjsua upon * occurence of various events. @@ -130,7 +190,7 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri, /** * Notify UI when invite state has changed. */ -void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e); +void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e); #endif /* __PJSUA_H__ */ |