summaryrefslogtreecommitdiff
path: root/pjsip
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-07 18:48:01 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-07 18:48:01 +0000
commit813cd5ef6791ddf9778ca61c08ce19f48202b3bc (patch)
tree4fcb5e984e0d06def8a8b1b3f4144bee31ac1ab6 /pjsip
parentda1de0865a23a7eb5cde2b4160979164dbc08a39 (diff)
Tested initial implementation: basic UAC, client registration, authentication, etc
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@141 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjsip')
-rw-r--r--pjsip/build/pjsip_core.dsp1
-rw-r--r--pjsip/build/pjsip_ua.dsp6
-rw-r--r--pjsip/build/pjsua.dsp23
-rw-r--r--pjsip/include/pjsip-ua/sip_inv.h1
-rw-r--r--pjsip/include/pjsip-ua/sip_regc.h38
-rw-r--r--pjsip/include/pjsip/sip_dialog.h1
-rw-r--r--pjsip/include/pjsip_ua.h2
-rw-r--r--pjsip/src/pjsip-ua/sip_inv.c75
-rw-r--r--pjsip/src/pjsip-ua/sip_reg.c213
-rw-r--r--pjsip/src/pjsip/sip_auth_client.c18
-rw-r--r--pjsip/src/pjsip/sip_dialog.c69
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c7
-rw-r--r--pjsip/src/pjsip/sip_transaction.c63
-rw-r--r--pjsip/src/pjsip/sip_ua_layer.c16
-rw-r--r--pjsip/src/pjsip/sip_util.c12
-rw-r--r--pjsip/src/pjsip/sip_util_statefull.c78
-rw-r--r--pjsip/src/pjsua/main.c494
-rw-r--r--pjsip/src/pjsua/pjsua.c216
-rw-r--r--pjsip/src/pjsua/pjsua.h66
19 files changed, 1149 insertions, 250 deletions
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(&regc->auth_sess_list);
+ status = pjsip_auth_clt_init(&regc->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, &regc->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(&regc->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, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info );
+ pjsip_auth_clt_init_req( &regc->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, &regc->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, &regc->auth_sess_list,
- regc->cred_count, regc->cred_info,
- tsx->last_tx, event->src.rdata );
+ status = pjsip_auth_clt_reinit_req( &regc->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__ */