(Major) Task #737 and #738: integration of APS-Direct and Audiodev from aps-direct branch to trunk.
git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@2506 74dad513-b988-da41-8d7b-12977e46ad98
diff --git a/build.symbian/symbian_ua.mmp b/build.symbian/symbian_ua.mmp
index 9521476..a96da6a 100644
--- a/build.symbian/symbian_ua.mmp
+++ b/build.symbian/symbian_ua.mmp
@@ -1,6 +1,6 @@
-#define SND_HAS_APS 1
+#define SND_HAS_APS 0
#define SND_HAS_VAS 0
-#define SND_HAS_MDA 0
+#define SND_HAS_MDA 1
TARGET symbian_ua.exe
TARGETTYPE exe
diff --git a/pjlib-util/src/pjlib-util/scanner.c b/pjlib-util/src/pjlib-util/scanner.c
index a5a5616..6e01ba6 100644
--- a/pjlib-util/src/pjlib-util/scanner.c
+++ b/pjlib-util/src/pjlib-util/scanner.c
@@ -28,7 +28,7 @@
#define PJ_SCAN_IS_SPACE(c) ((c)==' ' || (c)=='\t')
#define PJ_SCAN_IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
#define PJ_SCAN_IS_PROBABLY_SPACE(c) ((c) <= 32)
-#define PJ_SCAN_CHECK_EOF(s) (*s)
+#define PJ_SCAN_CHECK_EOF(s) (s != scanner->end)
#if defined(PJ_SCANNER_USE_BITWISE) && PJ_SCANNER_USE_BITWISE != 0
@@ -375,7 +375,7 @@
*/
do {
/* loop until end_quote is found. */
- while (*s && *s != '\n' && *s != end_quote[qpair]) {
+ while (PJ_SCAN_CHECK_EOF(s) && *s != '\n' && *s != end_quote[qpair]) {
++s;
}
diff --git a/pjlib/include/pj/os.h b/pjlib/include/pj/os.h
index 0bd1863..4a5a77f 100644
--- a/pjlib/include/pj/os.h
+++ b/pjlib/include/pj/os.h
@@ -394,6 +394,18 @@
*/
PJ_DECL(pj_status_t) pj_symbianos_set_params(pj_symbianos_params *prm);
+/**
+ * Notify PJLIB that the access point connection has been down or unusable
+ * and PJLIB should not try to access the Symbian socket API (especially ones
+ * that send packets). Sending packet when RConnection is reconnected to
+ * different access point may cause the WaitForRequest() for the function to
+ * block indefinitely.
+ *
+ * @param up If set to PJ_FALSE it will cause PJLIB to not try
+ * to access socket API, and error will be returned
+ * immediately instead.
+ */
+PJ_DECL(void) pj_symbianos_set_connection_status(pj_bool_t up);
/**
* @}
diff --git a/pjlib/src/pj/addr_resolv_symbian.cpp b/pjlib/src/pj/addr_resolv_symbian.cpp
index cfd8aa5..fbca108 100644
--- a/pjlib/src/pj/addr_resolv_symbian.cpp
+++ b/pjlib/src/pj/addr_resolv_symbian.cpp
@@ -71,6 +71,9 @@
PJ_ASSERT_RETURN(name && count && ai, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
// Get resolver for the specified address family
RHostResolver &resv = PjSymbianOS::Instance()->GetResolver(af);
diff --git a/pjlib/src/pj/ioqueue_symbian.cpp b/pjlib/src/pj/ioqueue_symbian.cpp
index ba4092b..ee6d7e0 100644
--- a/pjlib/src/pj/ioqueue_symbian.cpp
+++ b/pjlib/src/pj/ioqueue_symbian.cpp
@@ -635,6 +635,9 @@
TInetAddr inetAddr;
TRequestStatus reqStatus;
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
// Convert address
status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen,
inetAddr);
@@ -747,6 +750,9 @@
// Forcing pending operation is not supported.
PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
// Clear flag
flags &= ~PJ_IOQUEUE_ALWAYS_ASYNC;
@@ -785,6 +791,9 @@
// Forcing pending operation is not supported.
PJ_ASSERT_RETURN((flags & PJ_IOQUEUE_ALWAYS_ASYNC)==0, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
// Convert address
status = PjSymbianOS::pj2Addr(*(const pj_sockaddr*)addr, addrlen,
inetAddr);
diff --git a/pjlib/src/pj/os_core_symbian.cpp b/pjlib/src/pj/os_core_symbian.cpp
index 5fbf3d4..983ce88 100644
--- a/pjlib/src/pj/os_core_symbian.cpp
+++ b/pjlib/src/pj/os_core_symbian.cpp
@@ -151,7 +151,8 @@
//
PjSymbianOS::PjSymbianOS()
-: isSocketServInitialized_(false), isResolverInitialized_(false),
+: isConnectionUp_(false),
+ isSocketServInitialized_(false), isResolverInitialized_(false),
console_(NULL), selectTimeoutTimer_(NULL),
appSocketServ_(NULL), appConnection_(NULL), appHostResolver_(NULL),
appHostResolver6_(NULL)
@@ -229,6 +230,8 @@
isResolverInitialized_ = true;
}
+ isConnectionUp_ = true;
+
return KErrNone;
on_error:
@@ -239,6 +242,8 @@
// Shutdown
void PjSymbianOS::Shutdown()
{
+ isConnectionUp_ = false;
+
if (isResolverInitialized_) {
hostResolver_.Close();
#if defined(PJ_HAS_IPV6) && PJ_HAS_IPV6!=0
@@ -252,15 +257,16 @@
isSocketServInitialized_ = false;
}
- if (console_) {
- delete console_;
- console_ = NULL;
- }
+ delete console_;
+ console_ = NULL;
- if (selectTimeoutTimer_) {
- delete selectTimeoutTimer_;
- selectTimeoutTimer_ = NULL;
- }
+ delete selectTimeoutTimer_;
+ selectTimeoutTimer_ = NULL;
+
+ appSocketServ_ = NULL;
+ appConnection_ = NULL;
+ appHostResolver_ = NULL;
+ appHostResolver6_ = NULL;
}
// Convert to Unicode
@@ -306,6 +312,13 @@
}
+/* Set connection status */
+PJ_DEF(void) pj_symbianos_set_connection_status(pj_bool_t up)
+{
+ PjSymbianOS::Instance()->SetConnectionStatus(up != 0);
+}
+
+
/*
* pj_init(void).
* Init PJLIB!
diff --git a/pjlib/src/pj/os_symbian.h b/pjlib/src/pj/os_symbian.h
index 74ae7f0..3497e7a 100644
--- a/pjlib/src/pj/os_symbian.h
+++ b/pjlib/src/pj/os_symbian.h
@@ -313,6 +313,21 @@
}
}
+ //
+ // Return true if the access point connection is up
+ //
+ bool IsConnectionUp() const
+ {
+ return isConnectionUp_;
+ }
+
+ //
+ // Set access point connection status
+ //
+ void SetConnectionStatus(bool up)
+ {
+ isConnectionUp_ = up;
+ }
//
// Unicode Converter
@@ -353,6 +368,8 @@
}
private:
+ bool isConnectionUp_;
+
bool isSocketServInitialized_;
RSocketServ socketServ_;
@@ -374,6 +391,17 @@
PjSymbianOS();
};
+// This macro is used to check the access point connection status and return
+// failure if the AP connection is down or unusable. See the documentation
+// of pj_symbianos_set_connection_status() for more info
+#define PJ_SYMBIAN_CHECK_CONNECTION() \
+ PJ_SYMBIAN_CHECK_CONNECTION2(PJ_ECANCELLED)
+
+#define PJ_SYMBIAN_CHECK_CONNECTION2(retval) \
+ do { \
+ if (!PjSymbianOS::Instance()->IsConnectionUp()) \
+ return retval; \
+ } while (0);
#endif /* __OS_SYMBIAN_H__ */
diff --git a/pjlib/src/pj/sock_symbian.cpp b/pjlib/src/pj/sock_symbian.cpp
index a1bd0a3..a0ca80e 100644
--- a/pjlib/src/pj/sock_symbian.cpp
+++ b/pjlib/src/pj/sock_symbian.cpp
@@ -463,6 +463,9 @@
TRequestStatus reqStatus;
THostName tmpName;
+ // Return empty hostname if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION2(&hostname);
+
resv.GetHostName(tmpName, reqStatus);
User::WaitForRequest(reqStatus);
@@ -488,6 +491,9 @@
/* Sanity checks. */
PJ_ASSERT_RETURN(p_sock!=NULL, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
/* Set proto if none is specified. */
if (proto == 0) {
if (type == pj_SOCK_STREAM())
@@ -642,6 +648,9 @@
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
CPjSocket *pjSock = (CPjSocket*)sock;
RSocket &rSock = pjSock->Socket();
@@ -678,6 +687,9 @@
PJ_CHECK_STACK();
PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
CPjSocket *pjSock = (CPjSocket*)sock;
RSocket &rSock = pjSock->Socket();
@@ -717,6 +729,9 @@
PJ_ASSERT_RETURN(sock && buf && len, PJ_EINVAL);
PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
CPjSocket *pjSock = (CPjSocket*)sock;
RSocket &rSock = pjSock->Socket();
@@ -771,6 +786,9 @@
PJ_ASSERT_RETURN(*len > 0, PJ_EINVAL);
PJ_ASSERT_RETURN(*fromlen >= (int)sizeof(pj_sockaddr_in), PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
CPjSocket *pjSock = (CPjSocket*)sock;
RSocket &rSock = pjSock->Socket();
@@ -868,6 +886,9 @@
PJ_ASSERT_RETURN(((pj_sockaddr*)addr)->addr.sa_family == PJ_AF_INET,
PJ_EINVAL);
+ // Return failure if access point is marked as down by app.
+ PJ_SYMBIAN_CHECK_CONNECTION();
+
CPjSocket *pjSock = (CPjSocket*)sock;
RSocket &rSock = pjSock->Socket();
diff --git a/pjmedia/src/pjmedia-audiodev/wmme_dev.c b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
index aafce59..4c690eb 100644
--- a/pjmedia/src/pjmedia-audiodev/wmme_dev.c
+++ b/pjmedia/src/pjmedia-audiodev/wmme_dev.c
@@ -38,6 +38,14 @@
# pragma warning(pop)
#endif
+/* mingw lacks WAVE_FORMAT_ALAW/MULAW */
+#ifndef WAVE_FORMAT_ALAW
+# define WAVE_FORMAT_ALAW 0x0006
+#endif
+#ifndef WAVE_FORMAT_MULAW
+# define WAVE_FORMAT_MULAW 0x0007
+#endif
+
#if defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0
# pragma comment(lib, "Coredll.lib")
#elif defined(_MSC_VER)
diff --git a/pjmedia/src/pjmedia-codec/l16.c b/pjmedia/src/pjmedia-codec/l16.c
index 18baf67..d520d25 100644
--- a/pjmedia/src/pjmedia-codec/l16.c
+++ b/pjmedia/src/pjmedia-codec/l16.c
@@ -98,7 +98,11 @@
&l16_parse,
&l16_encode,
&l16_decode,
+#if !PLC_DISABLED
&l16_recover
+#else
+ NULL
+#endif
};
/* Definition for L16 codec factory operations. */
@@ -128,8 +132,8 @@
unsigned frame_size; /* Frame size, in bytes */
unsigned clock_rate; /* Clock rate */
- pj_bool_t plc_enabled;
#if !PLC_DISABLED
+ pj_bool_t plc_enabled;
pjmedia_plc *plc;
#endif
pj_bool_t vad_enabled;
@@ -526,9 +530,17 @@
static pj_status_t l16_open(pjmedia_codec *codec,
pjmedia_codec_param *attr )
{
- /* Nothing to do.. */
- PJ_UNUSED_ARG(codec);
- PJ_UNUSED_ARG(attr);
+ struct l16_data *data = NULL;
+
+ PJ_ASSERT_RETURN(codec && codec->codec_data && attr, PJ_EINVAL);
+
+ data = (struct l16_data*) codec->codec_data;
+
+ data->vad_enabled = (attr->setting.vad != 0);
+#if !PLC_DISABLED
+ data->plc_enabled = (attr->setting.plc != 0);
+#endif
+
return PJ_SUCCESS;
}
@@ -547,7 +559,9 @@
pj_assert(data != NULL);
data->vad_enabled = (attr->setting.vad != 0);
+#if !PLC_DISABLED
data->plc_enabled = (attr->setting.plc != 0);
+#endif
return PJ_SUCCESS;
}
diff --git a/pjmedia/src/pjmedia/jbuf.c b/pjmedia/src/pjmedia/jbuf.c
index c9a47f2..2cbb164 100644
--- a/pjmedia/src/pjmedia/jbuf.c
+++ b/pjmedia/src/pjmedia/jbuf.c
@@ -267,6 +267,13 @@
framelist->flist_max_count;
}
} else {
+ // check if frame is not too late, but watch out for sequence restart.
+ if (index < framelist->flist_origin &&
+ framelist->flist_origin - index < 0x7FFF)
+ {
+ return PJ_FALSE;
+ }
+
where = framelist->flist_tail;
framelist->flist_origin = index;
framelist->flist_tail = (framelist->flist_tail + 1) %
diff --git a/pjmedia/src/pjmedia/rtcp_xr.c b/pjmedia/src/pjmedia/rtcp_xr.c
index a4cc603..580924e 100644
--- a/pjmedia/src/pjmedia/rtcp_xr.c
+++ b/pjmedia/src/pjmedia/rtcp_xr.c
@@ -27,7 +27,7 @@
#include <pj/sock.h>
#include <pj/string.h>
-#if 1 //defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
#define THIS_FILE "rtcp_xr.c"
@@ -246,7 +246,7 @@
pj_uint32_t c31;
pj_uint32_t c32;
pj_uint32_t c33;
- pj_uint32_t ctotal, p32, p23, m;
+ pj_uint32_t ctotal, m;
unsigned est_extra_delay;
r = (pjmedia_rtcp_xr_rb_voip_mtc*) &sess->pkt.buf[size];
@@ -257,59 +257,79 @@
r->header.specific = 0;
r->header.length = pj_htons(8);
- /* Calculate additional transition counts. */
+ /* Use temp vars for easiness. */
c11 = sess->voip_mtc_stat.c11;
c13 = sess->voip_mtc_stat.c13;
c14 = sess->voip_mtc_stat.c14;
c22 = sess->voip_mtc_stat.c22;
c23 = sess->voip_mtc_stat.c23;
c33 = sess->voip_mtc_stat.c33;
+ m = sess->ptime * sess->frames_per_packet;
+
+ /* Calculate additional transition counts. */
c31 = c13;
c32 = c23;
ctotal = c11 + c14 + c13 + c22 + c23 + c31 + c32 + c33;
- m = sess->ptime * sess->frames_per_packet;
- /* Calculate burst and densities. */
- if (c11 && (c23 || c33)) {
- p32 = c32 / (c31 + c32 + c33);
- if((c22 + c23) < 1) {
- p23 = 1;
+ if (ctotal) {
+ pj_uint32_t p32, p23;
+
+ //original version:
+ //p32 = c32 / (c31 + c32 + c33);
+ if (c31 + c32 + c33 == 0)
+ p32 = 0;
+ else
+ p32 = (c32 << 16) / (c31 + c32 + c33);
+
+ //original version:
+ //if ((c22 + c23) < 1) {
+ // p23 = 1;
+ //} else {
+ // p23 = 1 - c22 / (c22 + c23);
+ //}
+ if (c23 == 0) {
+ p23 = 0;
} else {
- p23 = 1 - c22/(c22 + c23);
+ p23 = (c23 << 16) / (c22 + c23);
}
- sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)(256*p23/(p23 + p32));
- sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)(256*c14/(c11 + c14));
- /* Calculate burst and gap durations in ms */
- sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)((c11+c14+c13)*m/c13);
- sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t)(ctotal*m/c13 -
- sess->stat.rx.voip_mtc.gap_dur);
+ /* Calculate loss/discard densities, scaled of 0-256 */
+ if (c11 == 0)
+ sess->stat.rx.voip_mtc.gap_den = 0;
+ else
+ sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)
+ ((c14 << 8) / (c11 + c14));
+ if (p23 == 0)
+ sess->stat.rx.voip_mtc.burst_den = 0;
+ else
+ sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)
+ ((p23 << 8) / (p23 + p32));
+
+ /* Calculate (average) durations, in ms */
+ if (c13 == 0) {
+ c13 = 1;
+ ctotal += 1;
+ }
+ sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)
+ ((c11+c14+c13) * m / c13);
+ sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t)
+ ((ctotal - (c11+c14+c13)) * m / c13);
+
+ /* Callculate loss/discard rates, scaled 0-256 */
+ sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t)
+ ((sess->voip_mtc_stat.loss_count << 8) / ctotal);
+ sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t)
+ ((sess->voip_mtc_stat.discard_count << 8) / ctotal);
} else {
- /* No burst occurred yet until this time?
- * Just report full gap.
- */
- ctotal = sess->rtcp_session->stat.rx.pkt +
- sess->voip_mtc_stat.loss_count +
- sess->voip_mtc_stat.discard_count;
-
+ /* No lost/discarded packet yet. */
+ sess->stat.rx.voip_mtc.gap_den = 0;
sess->stat.rx.voip_mtc.burst_den = 0;
- sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)(256 *
- (sess->voip_mtc_stat.loss_count +
- sess->voip_mtc_stat.discard_count) /
- ctotal);
-
- /* Calculate burst and gap durations in ms */
- sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)((m*ctotal) < 0xFFFF?
- (m*ctotal) : 0xFFFF);
+ sess->stat.rx.voip_mtc.gap_dur = 0;
sess->stat.rx.voip_mtc.burst_dur = 0;
+ sess->stat.rx.voip_mtc.loss_rate = 0;
+ sess->stat.rx.voip_mtc.discard_rate = 0;
}
- /* Calculate loss and discard rates */
- sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t)
- (256 * sess->voip_mtc_stat.loss_count / ctotal);
- sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t)
- (256 * sess->voip_mtc_stat.discard_count / ctotal);
-
/* Set round trip delay (in ms) to RTT calculated after receiving
* DLRR or DLSR.
*/
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index fff8023..1e289e9 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -52,8 +52,6 @@
pjmedia_dir dir; /**< Channel direction. */
unsigned pt; /**< Payload type. */
pj_bool_t paused; /**< Paused?. */
- unsigned in_pkt_size; /**< Size of input buffer. */
- void *in_pkt; /**< Input buffer. */
unsigned out_pkt_size; /**< Size of output buffer. */
void *out_pkt; /**< Output buffer. */
pjmedia_rtp_session rtp; /**< RTP session. */
@@ -1508,12 +1506,6 @@
channel->paused = 1;
channel->pt = pt;
- /* Allocate buffer for incoming packet. */
-
- channel->in_pkt_size = PJMEDIA_MAX_MTU;
- channel->in_pkt = pj_pool_alloc( pool, channel->in_pkt_size );
- PJ_ASSERT_RETURN(channel->in_pkt != NULL, PJ_ENOMEM);
-
/* Allocate buffer for outgoing packet. */
@@ -1790,25 +1782,26 @@
#endif
/* Init jitter buffer parameters: */
- if (info->jb_max > 0)
- jb_max = info->jb_max;
+ if (info->jb_max >= stream->codec_param.info.frm_ptime)
+ jb_max = (info->jb_max + stream->codec_param.info.frm_ptime - 1) /
+ stream->codec_param.info.frm_ptime;
else
jb_max = 500 / stream->codec_param.info.frm_ptime;
- if (info->jb_min_pre > 0)
- jb_min_pre = info->jb_min_pre;
+ if (info->jb_min_pre >= stream->codec_param.info.frm_ptime)
+ jb_min_pre = info->jb_min_pre / stream->codec_param.info.frm_ptime;
else
//jb_min_pre = 60 / stream->codec_param.info.frm_ptime;
jb_min_pre = 1;
- if (info->jb_max_pre > 0)
- jb_max_pre = info->jb_max_pre;
+ if (info->jb_max_pre >= stream->codec_param.info.frm_ptime)
+ jb_max_pre = info->jb_max_pre / stream->codec_param.info.frm_ptime;
else
//jb_max_pre = 240 / stream->codec_param.info.frm_ptime;
jb_max_pre = jb_max * 4 / 5;
- if (info->jb_init > 0)
- jb_init = info->jb_init;
+ if (info->jb_init >= stream->codec_param.info.frm_ptime)
+ jb_init = info->jb_init / stream->codec_param.info.frm_ptime;
else
//jb_init = (jb_min_pre + jb_max_pre) / 2;
jb_init = 0;
diff --git a/pjnath/include/pjnath/stun_sock.h b/pjnath/include/pjnath/stun_sock.h
index dae0b30..5dcaad2 100644
--- a/pjnath/include/pjnath/stun_sock.h
+++ b/pjnath/include/pjnath/stun_sock.h
@@ -74,6 +74,12 @@
*/
PJ_STUN_SOCK_KEEP_ALIVE_OP,
+ /**
+ * IP address change notification from the keep-alive operation.
+ */
+ PJ_STUN_SOCK_MAPPED_ADDR_CHANGE
+
+
} pj_stun_sock_op;
diff --git a/pjnath/src/pjnath/ice_strans.c b/pjnath/src/pjnath/ice_strans.c
index c9ad487..3cb5f4e 100644
--- a/pjnath/src/pjnath/ice_strans.c
+++ b/pjnath/src/pjnath/ice_strans.c
@@ -1265,12 +1265,16 @@
}
break;
case PJ_STUN_SOCK_BINDING_OP:
+ case PJ_STUN_SOCK_MAPPED_ADDR_CHANGE:
if (status == PJ_SUCCESS) {
pj_stun_sock_info info;
status = pj_stun_sock_get_info(stun_sock, &info);
if (status == PJ_SUCCESS) {
char ipaddr[PJ_INET6_ADDRSTRLEN+10];
+ const char *op_name = (op==PJ_STUN_SOCK_BINDING_OP) ?
+ "Binding discovery complete" :
+ "srflx address changed";
pj_bool_t dup = PJ_FALSE;
/* Eliminate the srflx candidate if the address is
@@ -1308,9 +1312,9 @@
}
PJ_LOG(4,(comp->ice_st->obj_name,
- "Comp %d: Binding discovery complete, "
+ "Comp %d: %s, "
"srflx address is %s",
- comp->comp_id,
+ comp->comp_id, op_name,
pj_sockaddr_print(&info.mapped_addr, ipaddr,
sizeof(ipaddr), 3)));
diff --git a/pjnath/src/pjnath/stun_sock.c b/pjnath/src/pjnath/stun_sock.c
index 9bcd7c9..70c257a 100644
--- a/pjnath/src/pjnath/stun_sock.c
+++ b/pjnath/src/pjnath/stun_sock.c
@@ -669,11 +669,13 @@
pj_sockaddr_cp(&stun_sock->mapped_addr, &mapped_attr->sockaddr);
- resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS);
-
- goto on_return;
+ if (op==PJ_STUN_SOCK_KEEP_ALIVE_OP)
+ op = PJ_STUN_SOCK_MAPPED_ADDR_CHANGE;
}
+ /* Notify user */
+ resched = (*stun_sock->cb.on_status)(stun_sock, op, PJ_SUCCESS);
+
on_return:
/* Start/restart keep-alive timer */
if (resched)
diff --git a/pjsip-apps/src/pjsua/pjsua_app.c b/pjsip-apps/src/pjsua/pjsua_app.c
index 0fd1095..5944043 100644
--- a/pjsip-apps/src/pjsua/pjsua_app.c
+++ b/pjsip-apps/src/pjsua/pjsua_app.c
@@ -1488,6 +1488,14 @@
pj_strcat2(&cfg, "\n#\n# Network settings:\n#\n");
+ /* Nameservers */
+ for (i=0; i<config->cfg.nameserver_count; ++i) {
+ pj_ansi_sprintf(line, "--nameserver %.*s\n",
+ (int)config->cfg.nameserver[i].slen,
+ config->cfg.nameserver[i].ptr);
+ pj_strcat2(&cfg, line);
+ }
+
/* Outbound proxy */
for (i=0; i<config->cfg.outbound_proxy_cnt; ++i) {
pj_ansi_sprintf(line, "--outbound %.*s\n",
@@ -4015,6 +4023,97 @@
;
}
+/*****************************************************************************
+ * A simple module to handle otherwise unhandled request. We will register
+ * this with the lowest priority.
+ */
+
+/* Notification on incoming request */
+static pj_bool_t default_mod_on_rx_request(pjsip_rx_data *rdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_status_code status_code;
+ pj_status_t status;
+
+ /* Don't respond to ACK! */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_ack_method) == 0)
+ return PJ_TRUE;
+
+ /* Create basic response. */
+ if (pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_notify_method) == 0)
+ {
+ /* Unsolicited NOTIFY's, send with Bad Request */
+ status_code = PJSIP_SC_BAD_REQUEST;
+ } else {
+ /* Probably unknown method */
+ status_code = PJSIP_SC_METHOD_NOT_ALLOWED;
+ }
+ status = pjsip_endpt_create_response(pjsua_get_pjsip_endpt(),
+ rdata, status_code,
+ NULL, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "Unable to create response", status);
+ return PJ_TRUE;
+ }
+
+ /* Add Allow if we're responding with 405 */
+ if (status_code == PJSIP_SC_METHOD_NOT_ALLOWED) {
+ const pjsip_hdr *cap_hdr;
+ cap_hdr = pjsip_endpt_get_capability(pjsua_get_pjsip_endpt(),
+ PJSIP_H_ALLOW, NULL);
+ if (cap_hdr) {
+ pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_clone(tdata->pool,
+ cap_hdr));
+ }
+ }
+
+ /* Add User-Agent header */
+ {
+ pj_str_t user_agent;
+ char tmp[80];
+ const pj_str_t USER_AGENT = { "User-Agent", 10};
+ pjsip_hdr *h;
+
+ pj_ansi_snprintf(tmp, sizeof(tmp), "PJSUA v%s/%s",
+ pj_get_version(), PJ_OS_NAME);
+ pj_strdup2_with_null(tdata->pool, &user_agent, tmp);
+
+ h = (pjsip_hdr*) pjsip_generic_string_hdr_create(tdata->pool,
+ &USER_AGENT,
+ &user_agent);
+ pjsip_msg_add_hdr(tdata->msg, h);
+ }
+
+ pjsip_endpt_send_response2(pjsua_get_pjsip_endpt(), rdata, tdata,
+ NULL, NULL);
+
+ return PJ_TRUE;
+}
+
+
+/* The module instance. */
+static pjsip_module mod_default_handler =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-default-handler", 19 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION+99, /* Priority */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &default_mod_on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+
+
/*****************************************************************************
* Public API
@@ -4071,6 +4170,12 @@
if (status != PJ_SUCCESS)
return status;
+ /* Initialize our module to handle otherwise unhandled request */
+ status = pjsip_endpt_register_module(pjsua_get_pjsip_endpt(),
+ &mod_default_handler);
+ if (status != PJ_SUCCESS)
+ return status;
+
#ifdef STEREO_DEMO
stereo_demo();
#endif
diff --git a/pjsip-apps/src/symbian_ua/ua.cpp b/pjsip-apps/src/symbian_ua/ua.cpp
index b34e7fa..033a2f2 100644
--- a/pjsip-apps/src/symbian_ua/ua.cpp
+++ b/pjsip-apps/src/symbian_ua/ua.cpp
@@ -440,6 +440,7 @@
{
public:
ConsoleUI(CConsoleBase *con);
+ ~ConsoleUI();
// Run console UI
void Run();
@@ -465,6 +466,11 @@
CActiveScheduler::Add(this);
}
+ConsoleUI::~ConsoleUI()
+{
+ Stop();
+}
+
// Run console UI
void ConsoleUI::Run()
{
@@ -475,7 +481,7 @@
// Stop console UI
void ConsoleUI::Stop()
{
- DoCancel();
+ Cancel();
}
// Cancel asynchronous read.
@@ -875,6 +881,96 @@
#endif
+// Class CConnMon to monitor network connection (RConnection). Whenever
+// the connection is down, it will notify PJLIB and restart PJSUA-LIB.
+class CConnMon : public CActive {
+public:
+ static CConnMon* NewL(RConnection &conn, RSocketServ &sserver) {
+ CConnMon *self = new (ELeave) CConnMon(conn, sserver);
+ CleanupStack::PushL(self);
+ self->ConstructL();
+ CleanupStack::Pop(self);
+ return self;
+ }
+
+ void Start() {
+ conn_.ProgressNotification(nif_progress_, iStatus);
+ SetActive();
+ }
+
+ void Stop() {
+ Cancel();
+ }
+
+ ~CConnMon() { Stop(); }
+
+private:
+ CConnMon(RConnection &conn, RSocketServ &sserver) :
+ CActive(EPriorityHigh),
+ conn_(conn),
+ sserver_(sserver)
+ {
+ CActiveScheduler::Add(this);
+ }
+
+ void ConstructL() {}
+
+ void DoCancel() {
+ conn_.CancelProgressNotification();
+ }
+
+ void RunL() {
+ int stage = nif_progress_().iStage;
+
+ if (stage == KLinkLayerClosed) {
+ pj_status_t status;
+ TInt err;
+
+ // Tell pjlib that connection is down.
+ pj_symbianos_set_connection_status(PJ_FALSE);
+
+ PJ_LOG(3, (THIS_FILE, "RConnection closed, restarting PJSUA.."));
+
+ // Destroy pjsua
+ pjsua_destroy();
+ PJ_LOG(3, (THIS_FILE, "PJSUA destroyed."));
+
+ // Reopen the connection
+ err = conn_.Open(sserver_);
+ if (err == KErrNone)
+ err = conn_.Start();
+ if (err != KErrNone) {
+ CActiveScheduler::Stop();
+ return;
+ }
+
+ // Reinit Symbian OS param before pj_init()
+ pj_symbianos_params sym_params;
+ pj_bzero(&sym_params, sizeof(sym_params));
+ sym_params.rsocketserv = &sserver_;
+ sym_params.rconnection = &conn_;
+ pj_symbianos_set_params(&sym_params);
+
+ // Reinit pjsua
+ status = app_startup();
+ if (status != PJ_SUCCESS) {
+ pjsua_perror(THIS_FILE, "app_startup() error", status);
+ CActiveScheduler::Stop();
+ return;
+ }
+
+ PJ_LOG(3, (THIS_FILE, "PJSUA restarted."));
+ PrintMainMenu();
+ }
+
+ Start();
+ }
+
+ RConnection& conn_;
+ RSocketServ& sserver_;
+ TNifProgressBuf nif_progress_;
+};
+
////////////////////////////////////////////////////////////////////////////
int ua_main()
{
@@ -917,14 +1013,20 @@
return status;
}
+
// Run the UI
ConsoleUI *con = new ConsoleUI(console);
con->Run();
PrintMainMenu();
+ // Init & start connection monitor
+ CConnMon *connmon = CConnMon::NewL(aConn, aSocketServer);
+ connmon->Start();
+
CActiveScheduler::Start();
+ delete connmon;
delete con;
// Dump memory statistics
diff --git a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
index 0a09fcb..5128ca6 100644
--- a/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
+++ b/pjsip-apps/src/symbian_ua_gui/group/symbian_ua_gui.mmp
@@ -39,6 +39,7 @@
STATICLIBRARY pjsua_lib.lib pjsip_ua.lib
STATICLIBRARY pjsip_simple.lib pjsip.lib pjsdp.lib pjmedia.lib
+STATICLIBRARY pjmedia_audiodev.lib
STATICLIBRARY pjnath.lib pjlib_util.lib pjlib.lib
STATICLIBRARY libsrtp.lib
STATICLIBRARY libgsmcodec.lib
@@ -49,12 +50,10 @@
STATICLIBRARY null_audio.lib
CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
#elif SND_USE_APS
- STATICLIBRARY symbian_audio_aps.lib
- LIBRARY APSSession2.lib
+ LIBRARY APSSession2.lib
CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment MultimediaDD
- MACRO PJMEDIA_SYM_SND_USE_APS=1
+ MACRO PJMEDIA_SYM_SND_USE_APS=1
#else
- STATICLIBRARY symbian_audio.lib
LIBRARY mediaclientaudiostream.lib
LIBRARY mediaclientaudioinputstream.lib
CAPABILITY NetworkServices LocalServices ReadUserData WriteUserData UserEnvironment
diff --git a/pjsip/src/pjsip/sip_parser.c b/pjsip/src/pjsip/sip_parser.c
index 5a45817..bb470c7 100644
--- a/pjsip/src/pjsip/sip_parser.c
+++ b/pjsip/src/pjsip/sip_parser.c
@@ -54,8 +54,6 @@
*/
#define GENERIC_URI_CHARS "#?;:@&=+-_.!~*'()%$,/" "%"
-#define PJSIP_VERSION "SIP/2.0"
-
#define UNREACHED(expr)
#define IS_NEWLINE(c) ((c)=='\r' || (c)=='\n')
@@ -893,6 +891,36 @@
return NULL;
}
+/* SIP version */
+static void parse_sip_version(pj_scanner *scanner)
+{
+ pj_str_t SIP = { "SIP", 3 };
+ pj_str_t V2 = { "2.0", 3 };
+ pj_str_t sip, version;
+
+ pj_scan_get( scanner, &pconst.pjsip_ALPHA_SPEC, &sip);
+ if (pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
+ pj_scan_get_n( scanner, 3, &version);
+ if (pj_stricmp(&sip, &SIP) || pj_stricmp(&version, &V2))
+ on_syntax_error(scanner);
+}
+
+static pj_bool_t is_next_sip_version(pj_scanner *scanner)
+{
+ pj_str_t SIP = { "SIP", 3 };
+ pj_str_t sip;
+ int c;
+
+ c = pj_scan_peek(scanner, &pconst.pjsip_ALPHA_SPEC, &sip);
+ /* return TRUE if it is "SIP" followed by "/" or space.
+ * we include space since the "/" may be separated by space,
+ * although this would mean it would return TRUE if it is a
+ * request and the method is "SIP"!
+ */
+ return c && (c=='/' || c==' ' || c=='\t') && pj_stricmp(&sip, &SIP)==0;
+}
+
/* Internal function to parse SIP message */
static pjsip_msg *int_parse_msg( pjsip_parse_ctx *ctx,
pjsip_parser_err_report *err_list)
@@ -926,7 +954,7 @@
return NULL;
/* Parse request or status line */
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION, 7) == 0) {
+ if (is_next_sip_version(scanner)) {
msg = pjsip_msg_create(pool, PJSIP_RESPONSE_MSG);
int_parse_status_line( scanner, &msg->line.status );
} else {
@@ -1125,7 +1153,7 @@
}
-/* Parse parameter (";" pname ["=" pvalue]) in header. */
+/* Parse parameter (";" pname ["=" pvalue]) in SIP header. */
static void int_parse_param( pj_scanner *scanner, pj_pool_t *pool,
pj_str_t *pname, pj_str_t *pvalue,
unsigned option)
@@ -1513,9 +1541,7 @@
pjsip_method_init_np( &req_line->method, &token);
req_line->uri = int_parse_uri(scanner, pool, PJ_TRUE);
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW( PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n (scanner, 7, 1);
+ parse_sip_version(scanner);
pj_scan_get_newline( scanner );
}
@@ -1525,10 +1551,7 @@
{
pj_str_t token;
- if (pj_scan_stricmp_alnum(scanner, PJSIP_VERSION, 7) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- pj_scan_advance_n( scanner, 7, 1);
-
+ parse_sip_version(scanner);
pj_scan_get( scanner, &pconst.pjsip_DIGIT_SPEC, &token);
status_line->code = pj_strtoul(&token);
if (*scanner->curptr != '\r' && *scanner->curptr != '\n')
@@ -1618,12 +1641,32 @@
/* Parse generic string header. */
static void parse_generic_string_hdr( pjsip_generic_string_hdr *hdr,
- pj_scanner *scanner )
+ pjsip_parse_ctx *ctx)
{
- if (pj_cis_match(&pconst.pjsip_NOT_NEWLINE, *scanner->curptr))
+ pj_scanner *scanner = ctx->scanner;
+
+ hdr->hvalue.slen = 0;
+
+ /* header may be mangled hence the loop */
+ while (pj_cis_match(&pconst.pjsip_NOT_NEWLINE, *scanner->curptr)) {
+ pj_str_t next, tmp;
+
pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &hdr->hvalue);
- else
- hdr->hvalue.slen = 0;
+ if (IS_NEWLINE(*scanner->curptr))
+ break;
+ /* mangled, get next fraction */
+ pj_scan_get( scanner, &pconst.pjsip_NOT_NEWLINE, &next);
+ /* concatenate */
+ tmp.ptr = (char*)pj_pool_alloc(ctx->pool,
+ hdr->hvalue.slen + next.slen + 2);
+ tmp.slen = 0;
+ pj_strcpy(&tmp, &hdr->hvalue);
+ pj_strcat2(&tmp, " ");
+ pj_strcat(&tmp, &next);
+ tmp.ptr[tmp.slen] = '\0';
+
+ hdr->hvalue = tmp;
+ }
parse_hdr_end(scanner);
}
@@ -1934,13 +1977,12 @@
pj_str_t pname, pvalue;
//Parse with PARAM_CHAR instead, to allow IPv6
+ //No, back to using int_parse_param() for the "`" character!
//int_parse_param( scanner, pool, &pname, &pvalue, 0);
- /* Get ';' character */
- pj_scan_get_char(scanner);
-
- parse_param_imp(scanner, pool, &pname, &pvalue,
- &pconst.pjsip_PARAM_CHAR_SPEC,
- &pconst.pjsip_PARAM_CHAR_SPEC_ESC, 0);
+ //parse_param_imp(scanner, pool, &pname, &pvalue,
+ // &pconst.pjsip_TOKEN_SPEC,
+ // &pconst.pjsip_TOKEN_SPEC_ESC, 0);
+ int_parse_param(scanner, pool, &pname, &pvalue, 0);
if (!parser_stricmp(pname, pconst.pjsip_BRANCH_STR) && pvalue.slen) {
hdr->branch_param = pvalue;
@@ -2075,10 +2117,9 @@
else
pj_list_insert_before(first, hdr);
- if (pj_scan_stricmp_alnum( scanner, PJSIP_VERSION "/", 8) != 0)
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
-
- pj_scan_advance_n( scanner, 8, 1);
+ parse_sip_version(scanner);
+ if (pj_scan_get_char(scanner) != '/')
+ on_syntax_error(scanner);
pj_scan_get( scanner, &pconst.pjsip_TOKEN_SPEC, &hdr->transport);
int_parse_host(scanner, &hdr->sent_by.host);
@@ -2119,7 +2160,7 @@
pjsip_generic_string_hdr *hdr;
hdr = pjsip_generic_string_hdr_create(ctx->pool, NULL, NULL);
- parse_generic_string_hdr(hdr, ctx->scanner);
+ parse_generic_string_hdr(hdr, ctx);
return (pjsip_hdr*)hdr;
}
diff --git a/pjsip/src/pjsip/sip_tel_uri.c b/pjsip/src/pjsip/sip_tel_uri.c
index 42f8093..24525c8 100644
--- a/pjsip/src/pjsip/sip_tel_uri.c
+++ b/pjsip/src/pjsip/sip_tel_uri.c
@@ -442,6 +442,7 @@
}
scanner->skip_ws = skip_ws;
+ pj_scan_skip_whitespace(scanner);
return uri;
}
diff --git a/pjsip/src/pjsip/sip_transaction.c b/pjsip/src/pjsip/sip_transaction.c
index f99a1a4..5a6f7dc 100644
--- a/pjsip/src/pjsip/sip_transaction.c
+++ b/pjsip/src/pjsip/sip_transaction.c
@@ -1376,6 +1376,9 @@
pj_memcpy(&tsx->addr, &tsx->res_addr.addr, tsx->res_addr.addr_len);
tsx->addr_len = tsx->res_addr.addr_len;
tsx->is_reliable = PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport);
+ } else {
+ tsx->is_reliable =
+ (tsx->res_addr.dst_host.flag & PJSIP_TRANSPORT_RELIABLE);
}
@@ -2315,7 +2318,7 @@
*/
timeout = timeout_timer_val;
- } else if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ } else if (!tsx->is_reliable) {
/* For non-INVITE, start timer J at 64*T1 for unreliable
* transport.
@@ -2655,9 +2658,7 @@
/* Start Timer D with TD/T4 timer if unreliable transport is used. */
/* Note: tsx->transport may be NULL! */
- if ((tsx->transport && PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0)
- || ((tsx->transport_flag & PJSIP_TRANSPORT_RELIABLE) == 0))
- {
+ if (!tsx->is_reliable) {
if (tsx->method.id == PJSIP_INVITE_METHOD) {
timeout = td_timer_val;
} else {
@@ -2722,7 +2723,7 @@
/* Timer I is T4 timer for unreliable transports, and
* zero seconds for reliable transports.
*/
- if (PJSIP_TRANSPORT_IS_RELIABLE(tsx->transport)==0) {
+ if (!tsx->is_reliable) {
timeout.sec = 0;
timeout.msec = 0;
} else {
diff --git a/pjsip/src/pjsip/sip_util.c b/pjsip/src/pjsip/sip_util.c
index 18116c0..381ccad 100644
--- a/pjsip/src/pjsip/sip_util.c
+++ b/pjsip/src/pjsip/sip_util.c
@@ -859,12 +859,17 @@
PJSIP_ENOTREQUESTMSG);
PJ_ASSERT_RETURN(dest_info != NULL, PJ_EINVAL);
- /* Assert if the request contains strict route and strict
- * route processing has been applied before. We need to
- * restore the strict route with pjsip_restore_strict_route_set()
- * before we can call this function again, otherwise strict
- * route will be swapped twice!
+ /* If the request contains strict route, check that the strict route
+ * has been restored to its original values before processing the
+ * route set. The strict route is restored to the original values
+ * with pjsip_restore_strict_route_set(). If caller did not restore
+ * the strict route before calling this function, we need to call it
+ * here, or otherwise the strict-route and Request-URI will be swapped
+ * twice!
*/
+ if (tdata->saved_strict_route != NULL) {
+ pjsip_restore_strict_route_set(tdata);
+ }
PJ_ASSERT_RETURN(tdata->saved_strict_route==NULL, PJ_EBUG);
/* Find the first and last "Route" headers from the message. */
diff --git a/pjsip/src/pjsua-lib/pjsua_core.c b/pjsip/src/pjsua-lib/pjsua_core.c
index 84bfe20..7430fc1 100644
--- a/pjsip/src/pjsua-lib/pjsua_core.c
+++ b/pjsip/src/pjsua-lib/pjsua_core.c
@@ -89,7 +89,7 @@
{
pj_bzero(cfg, sizeof(*cfg));
- cfg->max_calls = 4;
+ cfg->max_calls = ((PJSUA_MAX_CALLS) < 4) ? (PJSUA_MAX_CALLS) : 4;
cfg->thread_cnt = 1;
cfg->nat_type_in_sdp = 1;
cfg->force_lr = PJ_TRUE;
@@ -838,8 +838,10 @@
pj_time_val_normalize(&timeout);
do {
- while (pjsua_handle_events(10) > 0)
- ;
+ int i;
+ i = msec / 10;
+ while (pjsua_handle_events(10) > 0 && i > 0)
+ --i;
pj_gettimeofday(&now);
} while (PJ_TIME_VAL_LT(now, timeout));
}
diff --git a/pjsip/src/test-pjsip/msg_test.c b/pjsip/src/test-pjsip/msg_test.c
index ad19b41..f825a98 100644
--- a/pjsip/src/test-pjsip/msg_test.c
+++ b/pjsip/src/test-pjsip/msg_test.c
@@ -824,7 +824,7 @@
{
char *hname;
char *hshort_name;
- char hcontent[1024];
+ char *hcontent;
int (*test)(pjsip_hdr*);
unsigned flags;
} hdr_test_data[] =
@@ -1734,6 +1734,11 @@
pj_pool_t *pool;
pjsip_hdr *parsed_hdr1=NULL, *parsed_hdr2=NULL;
char *input, *output;
+#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
+ static char hcontent[1024];
+#else
+ char *hcontent;
+#endif
int rc;
pool = pjsip_endpt_create_pool(endpt, NULL, POOL_SIZE, POOL_SIZE);
@@ -1741,8 +1746,15 @@
/* Parse the header */
hname = pj_str(test->hname);
len = strlen(test->hcontent);
+#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
+ PJ_ASSERT_RETURN(len < sizeof(hcontent), PJSIP_EMSGTOOLONG);
+ strcpy(hcontent, test->hcontent);
+#else
+ hcontent = test->hcontent;
+#endif
+
parsed_hdr1 = (pjsip_hdr*) pjsip_parse_hdr(pool, &hname,
- test->hcontent, len,
+ hcontent, len,
&parsed_len);
if (parsed_hdr1 == NULL) {
if (test->flags & HDR_FLAG_PARSE_FAIL) {
@@ -1765,7 +1777,14 @@
if (test->hshort_name) {
hname = pj_str(test->hshort_name);
len = strlen(test->hcontent);
- parsed_hdr2 = (pjsip_hdr*) pjsip_parse_hdr(pool, &hname, test->hcontent, len, &parsed_len);
+#if defined(PJSIP_UNESCAPE_IN_PLACE) && PJSIP_UNESCAPE_IN_PLACE!=0
+ PJ_ASSERT_RETURN(len < sizeof(hcontent), PJSIP_EMSGTOOLONG);
+ strcpy(hcontent, test->hcontent);
+#else
+ hcontent = test->hcontent;
+#endif
+
+ parsed_hdr2 = (pjsip_hdr*) pjsip_parse_hdr(pool, &hname, hcontent, len, &parsed_len);
if (parsed_hdr2 == NULL) {
PJ_LOG(3,(THIS_FILE, " error parsing header %s: %s", test->hshort_name, test->hcontent));
return -510;
diff --git a/tests/pjsua/inc_sip.py b/tests/pjsua/inc_sip.py
index 6d5ef05..0c05756 100644
--- a/tests/pjsua/inc_sip.py
+++ b/tests/pjsua/inc_sip.py
@@ -91,11 +91,7 @@
if self.trace_enabled:
print str(time.strftime("%H:%M:%S ")) + txt
- def create_req(self, method, sdp, branch="", extra_headers=""):
- if branch=="":
- self.cseq = self.cseq + 1
- msg = req_templ
- msg = msg.replace("$METHOD", method)
+ def update_fields(self, msg):
if self.tcp:
transport_param = ";transport=tcp"
else:
@@ -103,14 +99,23 @@
msg = msg.replace("$TARGET_URI", "sip:"+self.dst_addr+":"+str(self.dst_port) + transport_param)
msg = msg.replace("$LOCAL_IP", self.local_ip)
msg = msg.replace("$LOCAL_PORT", str(self.local_port))
- if branch=="":
- branch=str(random.random())
- msg = msg.replace("$BRANCH", branch)
msg = msg.replace("$FROM_TAG", self.local_tag)
msg = msg.replace("$TO_TAG", self.rem_tag)
msg = msg.replace("$CALL_ID", self.call_id)
msg = msg.replace("$CSEQ", str(self.cseq))
+ branch=str(random.random())
+ msg = msg.replace("$BRANCH", branch)
+ return msg
+
+ def create_req(self, method, sdp, branch="", extra_headers=""):
+ if branch=="":
+ self.cseq = self.cseq + 1
+ msg = req_templ
+ msg = msg.replace("$METHOD", method)
msg = msg.replace("$SIP_HEADERS", extra_headers)
+ if branch=="":
+ branch=str(random.random())
+ msg = msg.replace("$BRANCH", branch)
if sdp!="":
msg = msg.replace("$CONTENT_LENGTH", str(len(sdp)))
msg = msg + "Content-Type: application/sdp\r\n"
@@ -118,7 +123,7 @@
msg = msg.replace("$CONTENT_LENGTH", "0")
msg = msg + "\r\n"
msg = msg + sdp
- return msg
+ return self.update_fields(msg)
def create_response(self, request, code, reason, to_tag=""):
response = "SIP/2.0 " + str(code) + " " + reason + "\r\n"
diff --git a/tests/pjsua/mod_sendto.py b/tests/pjsua/mod_sendto.py
index 4cc12a4..a2f4521 100644
--- a/tests/pjsua/mod_sendto.py
+++ b/tests/pjsua/mod_sendto.py
@@ -19,7 +19,7 @@
cfg = cfg_file.sendto_cfg
if len(cfg.complete_msg) != 0:
- req = cfg.complete_msg
+ req = dlg.update_fields(cfg.complete_msg)
else:
req = dlg.create_invite(cfg.sdp, cfg.extra_headers)
resp = dlg.send_request_wait(req, 10)
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py
new file mode 100644
index 0000000..35f803c
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_1.py
@@ -0,0 +1,52 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.1. A Short Tortuous INVITE
+complete_msg = \
+"""INVITE sip:vivekg@chair-dnrc.example.com;unknownparam SIP/2.0
+TO :
+ sip:vivekg@chair-dnrc.example.com ; tag = 1918181833n
+from : "J Rosenberg \\\\\\"" <sip:jdrosen@example.com>
+ ;
+ tag = 98asjd8
+MaX-fOrWaRdS: 0068
+Call-ID: wsinv.ndaksdj@192.0.2.1
+Content-Length : 150
+cseq: 0009
+ INVITE
+Via : SIP / 2.0
+ /UDP
+ 192.0.2.2;rport;branch=390skdjuw
+s :
+NewFangledHeader: newfangled value
+ continued newfangled value
+UnknownHeaderWithUnusualValue: ;;,,;;,;
+Content-Type: application/sdp
+Route:
+ <sip:services.example.com;lr;unknownwith=value;unknown-no-value>
+v: SIP / 2.0 / TCP spindle.example.com ;
+ branch = z9hG4bK9ikj8 ,
+ SIP / 2.0 / UDP 192.168.255.111 ; branch=
+ z9hG4bK30239
+m:"Quoted string \\"\\"" <sip:jdrosen@example.com> ; newparam =
+ newvalue ;
+ secondparam ; q = 0.33
+
+v=0
+o=mhandley 29739 7272939 IN IP4 192.0.2.3
+s=-
+c=IN IP4 192.0.2.4
+t=0 0
+m=audio 49217 RTP/AVP 0 12
+m=video 3227 RTP/AVP 31
+a=rtpmap:31 LPC
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.1",
+ "--null-audio --auto-answer 200",
+ "", 481, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py
new file mode 100644
index 0000000..88fd249
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_2.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.2. Wide Range of Valid Characters
+complete_msg = \
+"""!interesting-Method0123456789_*+`.%indeed'~ sip:1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*:&it+has=1,weird!*pas$wo~d_too.(doesn't-it)@example.com SIP/2.0
+Via: SIP/2.0/UDP host1.example.com;rport;branch=z9hG4bK-.!%66*_+`'~
+To: "BEL:\\\x07 NUL:\\\x00 DEL:\\\x7F" <sip:1_unusual.URI~(to-be!sure)&isn't+it$/crazy?,/;;*@example.com>
+From: token1~` token2'+_ token3*%!.- <sip:mundane@example.com> ;fromParam''~+*_!.-%="\xD1\x80\xD0\xB0\xD0\xB1\xD0\xBE\xD1\x82\xD0\xB0\xD1\x8E\xD1\x89\xD0\xB8\xD0\xB9";tag=_token~1'+`*%!-.
+Call-ID: intmeth.word%ZK-!.*_+'@word`~)(><:\\/"][?}{
+CSeq: 139122385 !interesting-Method0123456789_*+`.%indeed'~
+Max-Forwards: 255
+extensionHeader-!.%*+_`'~: \xEF\xBB\xBF\xE5\xA4\xA7\xE5\x81\x9C\xE9\x9B\xBB
+Content-Length: 0
+
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.2",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py
new file mode 100644
index 0000000..4f32e97
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_3.py
@@ -0,0 +1,35 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.3. Valid Use of the % Escaping Mechanism
+complete_msg = \
+"""INVITE sip:sips%3Auser%40example.com@example.net SIP/2.0
+To: sip:%75se%72@example.com
+From: <sip:I%20have%20spaces@example.net>;tag=$FROM_TAG
+Max-Forwards: 87
+i: esc01.239409asdfakjkn23onasd0-3234
+CSeq: 234234 INVITE
+Via: SIP/2.0/UDP host5.example.net;rport;branch=z9hG4bKkdjuw
+C: application/sdp
+Contact:
+ <sip:cal%6Cer@$LOCAL_IP:$LOCAL_PORT;%6C%72;n%61me=v%61lue%25%34%31>
+Content-Length: 150
+
+v=0
+o=mhandley 29739 7272939 IN IP4 192.0.2.1
+s=-
+c=IN IP4 192.0.2.1
+t=0 0
+m=audio 49217 RTP/AVP 0 12
+m=video 3227 RTP/AVP 31
+a=rtpmap:31 LPC
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.3",
+ "--null-audio --auto-answer 200",
+ "", 200, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py
new file mode 100644
index 0000000..7a05c14
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_4.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.4. Escaped Nulls in URIs
+complete_msg = \
+"""REGISTER sip:example.com SIP/2.0
+To: sip:null-%00-null@example.com
+From: sip:null-%00-null@example.com;tag=839923423
+Max-Forwards: 70
+Call-ID: escnull.39203ndfvkjdasfkq3w4otrq0adsfdfnavd
+CSeq: 14398234 REGISTER
+Via: SIP/2.0/UDP host5.example.com;rport;branch=z9hG4bKkdjuw
+Contact: <sip:%00@host5.example.com>
+Contact: <sip:%00%00@host5.example.com>
+L:0
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.4",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py
new file mode 100644
index 0000000..2b449d1
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/001_torture_4475_3_1_1_5.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Torture message from RFC 4475
+# 3.1.1. Valid Messages
+# 3.1.1.5. Use of % When It Is Not an Escape
+complete_msg = \
+"""RE%47IST%45R sip:registrar.example.com SIP/2.0
+To: "%Z%45" <sip:resource@example.com>
+From: "%Z%45" <sip:resource@example.com>;tag=f232jadfj23
+Call-ID: esc02.asdfnqwo34rq23i34jrjasdcnl23nrlknsdf
+Via: SIP/2.0/TCP host.example.com;rport;branch=z9hG4bK209%fzsnel234
+CSeq: 29344 RE%47IST%45R
+Max-Forwards: 70
+Contact: <sip:alias1@host1.example.com>
+C%6Fntact: <sip:alias2@host2.example.com>
+Contact: <sip:alias3@host3.example.com>
+l: 0
+"""
+
+sendto_cfg = sip.SendtoCfg( "RFC 4475 3.1.1.5",
+ "--null-audio --auto-answer 200",
+ "", 405, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/110_tel_uri.py b/tests/pjsua/scripts-sendto/110_tel_uri.py
new file mode 100644
index 0000000..1d5e645
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/110_tel_uri.py
@@ -0,0 +1,46 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Handling of incoming tel: URI.
+complete_msg = \
+"""INVITE tel:+2065551212 SIP/2.0
+Via: SIP/2.0/UDP $LOCAL_IP:$LOCAL_PORT;rport;x-route-tag="tgrp:cococisco1";branch=z9hG4bK61E05
+From: <tel:12345>$FROM_TAG
+To: <tel:+2065551212>
+Date: Thu, 12 Feb 2009 18:32:33 GMT
+Call-ID: 58F8F7D6-F86A11DD-8013D591-5694EF79
+Supported: 100rel,timer,resource-priority
+Min-SE: 86400
+Cisco-Guid: 1492551325-4167700957-2148586897-1452601209
+User-Agent: Cisco-SIPGateway/IOS-12.x
+Allow: INVITE, OPTIONS, BYE, CANCEL, ACK, PRACK, UPDATE, REFER, SUBSCRIBE, NOTIFY, INFO, REGISTER
+CSeq: 101 INVITE
+Max-Forwards: 70
+Timestamp: 1234463553
+Contact: <tel:+1234;ext=1>
+Contact: <sip:tester@$LOCAL_IP:$LOCAL_PORT>
+Record-Route: <sip:tester@$LOCAL_IP:$LOCAL_PORT;lr>
+Expires: 180
+Allow-Events: telephone-event
+Content-Type: application/sdp
+Content-Disposition: session;handling=required
+Content-Length: 265
+
+v=0
+o=CiscoSystemsSIP-GW-UserAgent 1296 9529 IN IP4 X.X.X.X
+s=SIP Call
+c=IN IP4 $LOCAL_IP
+t=0 0
+m=audio 18676 RTP/AVP 0 101 19
+c=IN IP4 $LOCAL_IP
+a=rtpmap:0 PCMU/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16
+a=rtpmap:19 CN/8000
+a=ptime:20
+"""
+
+sendto_cfg = sip.SendtoCfg( "tel: URI", "--null-audio --auto-answer 200",
+ "", 200, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/159_no_rport.py b/tests/pjsua/scripts-sendto/159_no_rport.py
new file mode 100644
index 0000000..d34e4a9
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/159_no_rport.py
@@ -0,0 +1,38 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Ticket http://trac.pjsip.org/repos/ticket/718
+# RTC doesn't put rport in Via, and it is report to have caused segfault.
+complete_msg = \
+"""INVITE sip:localhost SIP/2.0
+Via: SIP/2.0/UDP $LOCAL_IP:$LOCAL_PORT;branch=z9hG4bK74a60ee5
+From: <sip:tester@localhost>;tag=as2858a32c
+To: <sip:pjsua@localhost>
+Contact: <sip:tester@$LOCAL_IP:$LOCAL_PORT>
+Call-ID: 123@localhost
+CSeq: 1 INVITE
+Max-Forwards: 70
+Content-Type: application/sdp
+Content-Length: 285
+
+v=0
+o=root 4236 4236 IN IP4 192.168.1.11
+s=session
+c=IN IP4 192.168.1.11
+t=0 0
+m=audio 14390 RTP/AVP 0 3 8 101
+a=rtpmap:0 PCMU/8000
+a=rtpmap:3 GSM/8000
+a=rtpmap:8 PCMA/8000
+a=rtpmap:101 telephone-event/8000
+a=fmtp:101 0-16
+a=silenceSupp:off - - - -
+a=ptime:20
+a=sendrecv
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RTC no rport", "--null-audio --auto-answer 200",
+ "", 200, complete_msg=complete_msg)
+
diff --git a/tests/pjsua/scripts-sendto/159_no_rport_nit.py b/tests/pjsua/scripts-sendto/159_no_rport_nit.py
new file mode 100644
index 0000000..2af9ab1
--- /dev/null
+++ b/tests/pjsua/scripts-sendto/159_no_rport_nit.py
@@ -0,0 +1,25 @@
+# $Id$
+import inc_sip as sip
+import inc_sdp as sdp
+
+# Ticket http://trac.pjsip.org/repos/ticket/718
+# RTC doesn't put rport in Via, and it is reported to have caused segfault.
+#
+complete_msg = \
+"""MESSAGE sip:localhost SIP/2.0
+Via: SIP/2.0/UDP localhost:$LOCAL_PORT;branch=z9hG4bK$BRANCH
+From: <sip:tester@localhost>;tag=as2858a32c
+To: <sip:pjsua@localhost>
+Call-ID: 123@localhost
+CSeq: 1 MESSAGE
+Max-Forwards: 70
+Content-Length: 11
+Content-Type: text/plain
+
+Hello world
+"""
+
+
+sendto_cfg = sip.SendtoCfg( "RTC no rport", "--null-audio --auto-answer 200",
+ "", 200, complete_msg=complete_msg)
+