summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-02-19 01:38:06 +0000
committerBenny Prijono <bennylp@teluu.com>2006-02-19 01:38:06 +0000
commit49a3b60593925562cbeb836a5885e034d2f78997 (patch)
tree87ae5502ec663309e0c78ad97cb22fd776896fc9
parent4e0f563feccb847c57739e48c91b0f5190938e9d (diff)
Initial SIMPLE implementation
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@197 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjsip/build/pjsip.dsw15
-rw-r--r--pjsip/build/pjsip_simple.dsp56
-rw-r--r--pjsip/build/pjsip_ua.dsp27
-rw-r--r--pjsip/build/pjsua.dsp20
-rw-r--r--pjsip/include/pjsip-simple/errno.h71
-rw-r--r--pjsip/include/pjsip-simple/event_notify.h330
-rw-r--r--pjsip/include/pjsip-simple/evsub.h421
-rw-r--r--pjsip/include/pjsip-simple/evsub_msg.h (renamed from pjsip/include/pjsip-simple/event_notify_msg.h)17
-rw-r--r--pjsip/include/pjsip-simple/messaging.h268
-rw-r--r--pjsip/include/pjsip-simple/pidf.h4
-rw-r--r--pjsip/include/pjsip-simple/presence.h269
-rw-r--r--pjsip/include/pjsip-simple/types.h30
-rw-r--r--pjsip/include/pjsip-simple/xpidf.h4
-rw-r--r--pjsip/include/pjsip/sip_config.h6
-rw-r--r--pjsip/include/pjsip/sip_errno.h5
-rw-r--r--pjsip/include/pjsip/sip_msg.h6
-rw-r--r--pjsip/include/pjsip/sip_types.h15
-rw-r--r--pjsip/include/pjsip_simple.h8
-rw-r--r--pjsip/src/pjsip-simple/event_notify.c1644
-rw-r--r--pjsip/src/pjsip-simple/evsub.c1785
-rw-r--r--pjsip/src/pjsip-simple/evsub_msg.c (renamed from pjsip/src/pjsip-simple/event_notify_msg.c)218
-rw-r--r--pjsip/src/pjsip-simple/messaging.c352
-rw-r--r--pjsip/src/pjsip-simple/pidf.c6
-rw-r--r--pjsip/src/pjsip-simple/presence.c1057
-rw-r--r--pjsip/src/pjsip-simple/xpidf.c5
-rw-r--r--pjsip/src/pjsip/sip_endpoint.c9
-rw-r--r--pjsip/src/pjsip/sip_errno.c1
-rw-r--r--pjsip/src/pjsip/sip_msg.c2
-rw-r--r--pjsip/src/pjsip/sip_ua_layer.c32
-rw-r--r--pjsip/src/pjsua/main.c277
-rw-r--r--pjsip/src/pjsua/pjsua.h70
-rw-r--r--pjsip/src/pjsua/pjsua_core.c89
-rw-r--r--pjsip/src/pjsua/pjsua_inv.c24
-rw-r--r--pjsip/src/pjsua/pjsua_opt.c13
-rw-r--r--pjsip/src/pjsua/pjsua_pres.c471
35 files changed, 4350 insertions, 3277 deletions
diff --git a/pjsip/build/pjsip.dsw b/pjsip/build/pjsip.dsw
index 48914e80..d9fc2ed5 100644
--- a/pjsip/build/pjsip.dsw
+++ b/pjsip/build/pjsip.dsw
@@ -93,6 +93,18 @@ Package=<4>
###############################################################################
+Project: "pjsip_simple"=.\pjsip_simple.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
Project: "pjsip_ua"=.\pjsip_ua.dsp - Package Owner=<4>
Package=<5>
@@ -134,6 +146,9 @@ Package=<4>
Begin Project Dependency
Project_Dep_Name pjlib_util
End Project Dependency
+ Begin Project Dependency
+ Project_Dep_Name pjsip_simple
+ End Project Dependency
}}}
###############################################################################
diff --git a/pjsip/build/pjsip_simple.dsp b/pjsip/build/pjsip_simple.dsp
index 5990238b..0739df59 100644
--- a/pjsip/build/pjsip_simple.dsp
+++ b/pjsip/build/pjsip_simple.dsp
@@ -32,16 +32,16 @@ RSC=rc.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir "./output/pjsip_simple_Win32_Release"
-# PROP BASE Intermediate_Dir "./output/pjsip_simple_Win32_Release"
+# PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
-# PROP Output_Dir "./output/pjsip_simple_Win32_Release"
-# PROP Intermediate_Dir "./output/pjsip_simple_Win32_Release"
+# PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-release"
+# PROP Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W3 /GX /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
+# ADD CPP /nologo /MD /W3 /GX /O2 /I "../include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
@@ -49,22 +49,22 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjsip_simple_vc6.lib"
+# ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-release.lib"
!ELSEIF "$(CFG)" == "pjsip_simple - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir "./output/pjsip_simple_Win32_Debug"
-# PROP BASE Intermediate_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
-# PROP Output_Dir "./output/pjsip_simple_Win32_Debug"
-# PROP Intermediate_Dir "./output/pjsip_simple_Win32_Debug"
+# PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug"
+# PROP Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
+# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /YX /FD /GZ /c
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
@@ -72,7 +72,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjsip_simple_vc6d.lib"
+# ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-debug.lib"
!ENDIF
@@ -85,27 +85,23 @@ LIB32=link.exe -lib
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Source File
-SOURCE=..\src\pjsip_simple\event_notify.c
+SOURCE="..\src\pjsip-simple\evsub.c"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\event_notify_msg.c
+SOURCE="..\src\pjsip-simple\evsub_msg.c"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\messaging.c
+SOURCE="..\src\pjsip-simple\pidf.c"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\pidf.c
+SOURCE="..\src\pjsip-simple\presence.c"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\presence.c
-# End Source File
-# Begin Source File
-
-SOURCE=..\src\pjsip_simple\xpidf.c
+SOURCE="..\src\pjsip-simple\xpidf.c"
# End Source File
# End Group
# Begin Group "Header Files"
@@ -113,31 +109,35 @@ SOURCE=..\src\pjsip_simple\xpidf.c
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
-SOURCE=..\src\pjsip_simple\event_notify.h
+SOURCE="..\include\pjsip-simple\errno.h"
+# End Source File
+# Begin Source File
+
+SOURCE="..\include\pjsip-simple\evsub.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\event_notify_msg.h
+SOURCE="..\include\pjsip-simple\evsub_msg.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\messaging.h
+SOURCE="..\include\pjsip-simple\pidf.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\pidf.h
+SOURCE=..\include\pjsip_simple.h
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple.h
+SOURCE="..\include\pjsip-simple\presence.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\presence.h
+SOURCE="..\include\pjsip-simple\types.h"
# End Source File
# Begin Source File
-SOURCE=..\src\pjsip_simple\xpidf.h
+SOURCE="..\include\pjsip-simple\xpidf.h"
# End Source File
# End Group
# End Target
diff --git a/pjsip/build/pjsip_ua.dsp b/pjsip/build/pjsip_ua.dsp
index d2edb14c..b1269b89 100644
--- a/pjsip/build/pjsip_ua.dsp
+++ b/pjsip/build/pjsip_ua.dsp
@@ -32,13 +32,13 @@ RSC=rc.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjsip_ua_vc6_Release"
-# PROP BASE Intermediate_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjsip_ua_vc6_Release"
-# PROP Intermediate_Dir ".\output\pjsip_ua_vc6_Release"
+# PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release"
+# PROP Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-release"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c
@@ -50,19 +50,19 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjsip_ua_vc6s.lib"
+# ADD LIB32 /nologo /out:"../lib/pjsip-ua-i386-win32-vc6-release.lib"
!ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjsip_ua_vc6_Debug"
-# PROP BASE Intermediate_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjsip_ua_vc6_Debug"
-# PROP Intermediate_Dir ".\output\pjsip_ua_vc6_Debug"
+# PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug"
+# PROP Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-debug"
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
@@ -74,7 +74,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LIB32=link.exe -lib
# ADD BASE LIB32 /nologo
-# ADD LIB32 /nologo /out:"../lib/pjsip_ua_vc6sd.lib"
+# ADD LIB32 /nologo /out:"../lib/pjsip-ua-i386-win32-vc6-debug.lib"
!ENDIF
@@ -92,13 +92,6 @@ SOURCE="..\src\pjsip-ua\sip_inv.c"
# Begin Source File
SOURCE="..\src\pjsip-ua\sip_reg.c"
-
-!IF "$(CFG)" == "pjsip_ua - Win32 Release"
-
-!ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug"
-
-!ENDIF
-
# End Source File
# End Group
# Begin Group "Header Files"
diff --git a/pjsip/build/pjsua.dsp b/pjsip/build/pjsua.dsp
index 73fc7a57..604c9939 100644
--- a/pjsip/build/pjsua.dsp
+++ b/pjsip/build/pjsua.dsp
@@ -32,13 +32,13 @@ RSC=rc.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
-# PROP BASE Output_Dir ".\output\pjsua_vc6_Release"
-# PROP BASE Intermediate_Dir ".\output\pjsua_vc6_Release"
+# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-release"
+# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
-# PROP Output_Dir ".\output\pjsua_vc6_Release"
-# PROP Intermediate_Dir ".\output\pjsua_vc6_Release"
+# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-release"
+# PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
@@ -58,13 +58,13 @@ LINK32=link.exe
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
-# PROP BASE Output_Dir ".\output\pjsua_vc6_Debug"
-# PROP BASE Intermediate_Dir ".\output\pjsua_vc6_Debug"
+# PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-debug"
+# PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
-# PROP Output_Dir ".\output\pjsua_vc6_Debug"
-# PROP Intermediate_Dir ".\output\pjsua_vc6_Debug"
+# PROP Output_Dir ".\output\pjsua-i386-win32-vc6-debug"
+# PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
@@ -110,6 +110,10 @@ SOURCE=..\src\pjsua\pjsua_opt.c
# End Source File
# Begin Source File
+SOURCE=..\src\pjsua\pjsua_pres.c
+# End Source File
+# Begin Source File
+
SOURCE=..\src\pjsua\pjsua_reg.c
# End Source File
# End Group
diff --git a/pjsip/include/pjsip-simple/errno.h b/pjsip/include/pjsip-simple/errno.h
new file mode 100644
index 00000000..a09def7b
--- /dev/null
+++ b/pjsip/include/pjsip-simple/errno.h
@@ -0,0 +1,71 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_ERRNO_H__
+#define __PJSIP_SIMPLE_ERRNO_H__
+
+
+#include <pjsip/sip_errno.h>
+
+/**
+ * @hideinitializer
+ * No event package with the specified name.
+ */
+#define PJSIP_SIMPLE_ENOPKG -1
+/**
+ * @hideinitializer
+ * Event package already exists.
+ */
+#define PJSIP_SIMPLE_EPKGEXISTS -1
+
+
+/**
+ * @hideinitializer
+ * Expecting SUBSCRIBE request
+ */
+#define PJSIP_SIMPLE_ENOTSUBSCRIBE -1
+/**
+ * @hideinitializer
+ * No presence associated with subscription
+ */
+#define PJSIP_SIMPLE_ENOPRESENCE -1
+/**
+ * @hideinitializer
+ * No presence info in server subscription
+ */
+#define PJSIP_SIMPLE_ENOPRESENCEINFO -1
+/**
+ * @hideinitializer
+ * Bad Content-Type
+ */
+#define PJSIP_SIMPLE_EBADCONTENT -1
+/**
+ * @hideinitializer
+ * Bad PIDF Message
+ */
+#define PJSIP_SIMPLE_EBADPIDF -1
+/**
+ * @hideinitializer
+ * Bad XPIDF Message
+ */
+#define PJSIP_SIMPLE_EBADXPIDF -1
+
+
+
+#endif /* __PJSIP_SIMPLE_ERRNO_H__ */
+
diff --git a/pjsip/include/pjsip-simple/event_notify.h b/pjsip/include/pjsip-simple/event_notify.h
deleted file mode 100644
index 5f002582..00000000
--- a/pjsip/include/pjsip-simple/event_notify.h
+++ /dev/null
@@ -1,330 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-#define __PJSIP_SIMPLE_EVENT_NOTIFY_H__
-
-/**
- * @file event_notify.h
- * @brief SIP Specific Event Notification Extension (RFC 3265)
- */
-
-#include <pjsip/sip_types.h>
-#include <pjsip/sip_auth.h>
-#include <pjsip_simple/event_notify_msg.h>
-#include <pj/timer.h>
-
-/**
- * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for SIP Specific
- * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
- * NOTIFY methods.
- *
- * This module itself is extensible; new event packages can be registered to
- * this module to handle specific extensions (such as presence).
- */
-
-PJ_BEGIN_DECL
-
-typedef struct pjsip_event_sub_cb pjsip_event_sub_cb;
-typedef struct pjsip_event_sub pjsip_event_sub;
-
-/**
- * This enumeration describes subscription state as described in the RFC 3265.
- * The standard specifies that extensions may define additional states. In the
- * case where the state is not known, the subscription state will be set to
- * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str
- * member of the susbcription structure.
- */
-typedef enum pjsip_event_sub_state
-{
- /** State is NULL. */
- PJSIP_EVENT_SUB_STATE_NULL,
-
- /** Subscription is active. */
- PJSIP_EVENT_SUB_STATE_ACTIVE,
-
- /** Subscription is pending. */
- PJSIP_EVENT_SUB_STATE_PENDING,
-
- /** Subscription is terminated. */
- PJSIP_EVENT_SUB_STATE_TERMINATED,
-
- /** Subscription state can not be determined. Application can query
- * the state information in state_str member.
- */
- PJSIP_EVENT_SUB_STATE_UNKNOWN,
-
-} pjsip_event_sub_state;
-
-/**
- * This structure describes notification to be called when incoming SUBSCRIBE
- * request is received. The module will call the callback registered by package
- * that matches the event description in the incoming SUBSCRIBE.
- */
-typedef struct pjsip_event_sub_pkg_cb
-{
- /**
- * This callback is called to first enquery the package whether it wants
- * to accept incoming SUBSCRIBE request. If it does, then on_subscribe
- * will be called.
- *
- * @param rdata The incoming request.
- * @param status The status code to be returned back to subscriber.
- */
- void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status);
-
- /**
- * This callback is called when the module receives incoming SUBSCRIBE
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received buffer.
- * @param cb Callback to be registered to the subscription instance.
- * @param expires The expiration to be set.
- */
- void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-
-} pjsip_event_sub_pkg_cb;
-
-/**
- * This structure describes callback that is registered by application or
- * package to receive notifications about a subscription.
- */
-struct pjsip_event_sub_cb
-{
- /**
- * This callback is used by both subscriber and notifier. It is called
- * when the subscription has been terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason);
-
- /**
- * This callback is called when we received SUBSCRIBE request to refresh
- * the subscription.
- *
- * @param sub The subscription instance.
- * @param rdata The received SUBSCRIBE request.
- */
- void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response on
- * previously sent SUBSCRIBE request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event);
-
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param sub The subscription instance.
- * @param rdata The received data.
- */
- void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
- /**
- * This callback is called when the module receives final response to
- * previously sent NOTIFY request.
- *
- * @param sub The subscription instance.
- * @param event The event.
- */
- void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event);
-
-};
-
-/**
- * This structure describes an event subscription record. The structure is used
- * to represent both subscriber and notifier.
- */
-struct pjsip_event_sub
-{
- pj_pool_t *pool; /**< Pool. */
- pjsip_endpoint *endpt; /**< Endpoint. */
- pjsip_event_sub_cb cb; /**< Callback. */
- pj_mutex_t *mutex; /**< Mutex. */
- pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */
- pjsip_event_sub_state state; /**< Subscription state. */
- pj_str_t state_str; /**< String describing the state. */
- pjsip_from_hdr *from; /**< Cached local info (From) */
- pjsip_to_hdr *to; /**< Cached remote info (To) */
- pjsip_contact_hdr *contact; /**< Cached local contact. */
- pjsip_cid_hdr *call_id; /**< Cached Call-ID */
- int cseq; /**< Outgoing CSeq */
- pjsip_event_hdr *event; /**< Event description. */
- pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */
- pjsip_accept_hdr *local_accept; /**< Local Accept header. */
- pjsip_route_hdr route_set; /**< Route-set. */
-
- pj_str_t key; /**< Key in the hash table. */
- void *user_data; /**< Application data. */
- int default_interval; /**< Refresh interval. */
- pj_timer_entry timer; /**< Internal timer. */
- pj_time_val expiry_time; /**< Time when subscription expires. */
- int pending_tsx; /**< Number of pending transactions. */
- pj_bool_t delete_flag; /**< Pending deletion flag. */
-
- pjsip_auth_session auth_sess; /**< Authorization sessions. */
- unsigned cred_cnt; /**< Number of credentials. */
- pjsip_cred_info *cred_info; /**< Array of credentials. */
-};
-
-
-
-
-/**
- * Initialize the module and get the instance of the module to be registered to
- * endpoint.
- *
- * @return The module instance.
- */
-PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void);
-
-
-/**
- * Register event package.
- *
- * @param event The event identification for the package.
- * @param accept_cnt Number of strings in Accept array.
- * @param accept Array of Accept value.
- * @param cb Callback to receive incoming SUBSCRIBE for the package.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb );
-
-
-/**
- * Create initial subscription instance (client).
- *
- * @param endpt The endpoint.
- * @param from URL to put in From header.
- * @param to The target resource.
- * @param event Event package.
- * @param expires Expiration time.
- * @param accept Accept specification.
- * @param user_data Application data to attach to this subscription.
- *
- * @return New client subscription instance.
- */
-PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb);
-
-/**
- * Set credentials to be used for outgoing request messages.
- *
- * @param sub Subscription instance.
- * @param count Number of credentials.
- * @param cred Array of credential info.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[]);
-
-/**
- * Set route set for outgoing requests.
- *
- * @param sub Subscription instance.
- * @param route_set List of route headers.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set );
-
-
-/**
- * Send SUBSCRIBE request.
- *
- * @param sub Subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub );
-
-/**
- * Terminate subscription (client). This will send unsubscription request to
- * notifier.
- *
- * @param sub Client subscription instance.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub );
-
-
-/**
- * For notifier, send NOTIFY request to subscriber, and set the state of
- * the subscription.
- *
- * @param sub The server subscription (notifier) instance.
- * @param state New state to set.
- * @param reason Specify reason if new state is terminated, otherwise
- * put NULL.
- * @param type Description of content type.
- * @param body Text body to send with the NOTIFY, or NULL if the
- * NOTIFY request should not contain any message body.
- *
- * @return Zero on success.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub,
- pjsip_event_sub_state state,
- const pj_str_t *reason,
- pjsip_msg_body *body);
-
-/**
- * Destroy subscription instance.
- *
- * @param sub The client or server subscription instance.
- *
- * @return Zero on success, one if the subscription will be
- * deleted automatically later, or -1 on error.
- */
-PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub);
-
-
-PJ_END_DECL
-
-/**
- * @}
- */
-
-#endif /* __PJSIP_SIMPLE_EVENT_NOTIFY_H__ */
diff --git a/pjsip/include/pjsip-simple/evsub.h b/pjsip/include/pjsip-simple/evsub.h
new file mode 100644
index 00000000..ee6315ee
--- /dev/null
+++ b/pjsip/include/pjsip-simple/evsub.h
@@ -0,0 +1,421 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_EVSUB_H__
+#define __PJSIP_SIMPLE_EVSUB_H__
+
+/**
+ * @file event_notify.h
+ * @brief SIP Specific Event Notification Extension (RFC 3265)
+ */
+
+#include <pjsip-simple/types.h>
+
+
+/**
+ * @defgroup PJSIP_EVENT_NOT SIP Event Notification (RFC 3265) Module
+ * @ingroup PJSIP_SIMPLE
+ * @{
+ *
+ * This module provides the implementation of SIP Extension for SIP Specific
+ * Event Notification (RFC 3265). It extends PJSIP by supporting SUBSCRIBE and
+ * NOTIFY methods.
+ *
+ * This module itself is extensible; new event packages can be registered to
+ * this module to handle specific extensions (such as presence).
+ */
+
+PJ_BEGIN_DECL
+
+
+/**
+ * Opaque type for event subscription session.
+ */
+typedef struct pjsip_evsub pjsip_evsub;
+
+
+/**
+ * This enumeration describes basic subscription state as described in the
+ * RFC 3265. The standard specifies that extensions may define additional
+ * states. In the case where the state is not known, the subscription state
+ * will be set to PJSIP_EVSUB_STATE_UNKNOWN, and the token will be kept
+ * in state_str member of the susbcription structure.
+ */
+enum pjsip_evsub_state
+{
+ PJSIP_EVSUB_STATE_NULL, /**< State is NULL. */
+ PJSIP_EVSUB_STATE_SENT, /**< Client has sent SUBSCRIBE request. */
+ PJSIP_EVSUB_STATE_ACCEPTED, /**< 2xx response to SUBSCRIBE has been
+ sent/received. */
+ PJSIP_EVSUB_STATE_PENDING, /**< Subscription is pending. */
+ PJSIP_EVSUB_STATE_ACTIVE, /**< Subscription is active. */
+ PJSIP_EVSUB_STATE_TERMINATED,/**< Subscription is terminated. */
+ PJSIP_EVSUB_STATE_UNKNOWN, /**< Subscription state can not be determined.
+ Application can query the state by
+ calling #pjsip_evsub_get_state_name().*/
+};
+
+/**
+ * @see pjsip_evsub_state
+ */
+typedef enum pjsip_evsub_state pjsip_evsub_state;
+
+
+
+/**
+ * This structure describes callback that is registered by application or
+ * package to receive notifications about subscription events.
+ */
+struct pjsip_evsub_user
+{
+ /**
+ * This callback is called when subscription state has changed.
+ * Application MUST be prepared to receive NULL event and events with
+ * type other than PJSIP_EVENT_TSX_STATE
+ *
+ * This callback is OPTIONAL.
+ *
+ * @param sub The subscription instance.
+ * @param event The event that has caused the state to change,
+ * which may be NULL or may have type other than
+ * PJSIP_EVENT_TSX_STATE.
+ */
+ void (*on_evsub_state)( pjsip_evsub *sub, pjsip_event *event);
+
+ /**
+ * This callback is called when transaction state has changed.
+ *
+ * @param sub The subscription instance.
+ * @param tsx Transaction.
+ * @param event The event.
+ */
+ void (*on_tsx_state)(pjsip_evsub *sub, pjsip_transaction *tsx,
+ pjsip_event *event);
+
+ /**
+ * This callback is called when incoming SUBSCRIBE (or any method that
+ * establishes the subscription in the first place) is received. It
+ * allows application to specify what response should be sent to
+ * remote, along with additional headers and message body to be put
+ * in the response.
+ *
+ * This callback is OPTIONAL.
+ *
+ * However, implementation MUST send NOTIFY request upon receiving this
+ * callback. The suggested behavior is to call
+ * #pjsip_evsub_last_notify(), since this function takes care
+ * about unsubscription request and calculates the appropriate expiration
+ * interval.
+ */
+ void (*on_rx_refresh)( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body);
+
+ /**
+ * This callback is called when client/subscriber received incoming
+ * NOTIFY request. It allows the application to specify what response
+ * should be sent to remote, along with additional headers and message
+ * body to be put in the response.
+ *
+ * This callback is OPTIONAL. When it is not implemented, the default
+ * behavior is to respond incoming NOTIFY request with 200 (OK).
+ *
+ * @param sub The subscription instance.
+ * @param rdata The received NOTIFY request.
+ * @param p_st_code Application MUST set the value of this argument with
+ * final status code (200-699) upon returning from the
+ * callback.
+ * @param p_st_text Custom status text, if any.
+ * @param res_hdr Upon return, application can put additional headers
+ * to be sent in the response in this list.
+ * @param p_body Application MAY specify message body to be sent in
+ * the response.
+ */
+ void (*on_rx_notify)(pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body);
+
+ /**
+ * This callback is called when it is time for the client to refresh
+ * the subscription.
+ *
+ * This callback is OPTIONAL. When it is not implemented, the default
+ * behavior is to refresh subscription by sending SUBSCRIBE with the
+ * interval set to current/last interval.
+ *
+ * @param sub The subscription instance.
+ */
+ void (*on_client_refresh)(pjsip_evsub *sub);
+
+ /**
+ * This callback is called when server doesn't receive subscription
+ * refresh after the specified subscription interval.
+ *
+ * This callback is OPTIONAL. When it is not implemented, the default
+ * behavior is to send NOTIFY to terminate the subscription.
+ */
+ void (*on_server_timeout)(pjsip_evsub *sub);
+
+};
+
+
+/**
+ * @see pjsip_evsub_user
+ */
+typedef struct pjsip_evsub_user pjsip_evsub_user;
+
+
+/**
+ * SUBSCRIBE method constant.
+ */
+extern const pjsip_method pjsip_subscribe_method;
+
+/**
+ * NOTIFY method constant.
+ */
+extern const pjsip_method pjsip_notify_method;
+
+
+
+/**
+ * Initialize the event subscription module and register the module to the
+ * specified endpoint.
+ *
+ * @param endpt The endpoint instance.
+ *
+ * @return PJ_SUCCESS if module can be created and registered
+ * successfully.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt);
+
+
+/**
+ * Get the event subscription module instance that was previously created
+ * and registered to endpoint.
+ *
+ * @return The event subscription module instance.
+ */
+PJ_DECL(pjsip_module*) pjsip_evsub_instance(void);
+
+
+/**
+ * Register event package to the event subscription framework.
+ *
+ * @param pkg_mod The module that implements the event package being
+ * registered.
+ * @param event_name Event package identification.
+ * @param expires Default subscription expiration time, in seconds.
+ * @param accept_cnt Number of strings in Accept array.
+ * @param accept Array of Accept value.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
+ const pj_str_t *event_name,
+ unsigned expires,
+ unsigned accept_cnt,
+ const pj_str_t accept[]);
+
+
+/**
+ * Create client subscription session.
+ *
+ * @param dlg The underlying dialog to use.
+ * @param user_cb Callback to receive event subscription notifications.
+ * @param event Event name.
+ * @param p_evsub Pointer to receive event subscription instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ const pj_str_t *event,
+ pjsip_evsub **p_evsub);
+
+/**
+ * Create server subscription session.
+ *
+ * @param dlg The underlying dialog to use.
+ * @param user_cb Callback to receive event subscription notifications.
+ * @param rdata The incoming request that creates the event
+ * subscription, such as SUBSCRIBE or REFER.
+ * @param p_evsub Pointer to receive event subscription instance.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_rx_data *rdata,
+ pjsip_evsub **p_evsub);
+
+
+/**
+ * Get subscription state.
+ *
+ * @param sub Event subscription instance.
+ *
+ * @return Subscription state.
+ */
+PJ_DECL(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub);
+
+
+/**
+ * Get the string representation of the subscription state.
+ *
+ * @param sub Event subscription instance.
+ *
+ * @return NULL terminated string.
+ */
+PJ_DECL(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub);
+
+
+/**
+ * Call this function to create request to initiate subscription, to
+ * refresh subcription, or to request subscription termination.
+ *
+ * @param sub Client subscription instance.
+ * @param method The method that establishes the subscription, such as
+ * SUBSCRIBE or REFER. If this argument is NULL, then
+ * SUBSCRIBE will be used.
+ * @param expires Subscription expiration. If the value is set to zero,
+ * this will request unsubscription. If the value is
+ * negative, default expiration as defined by the package
+ * will be used.
+ * @param p_tdata Pointer to receive the request.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
+ const pjsip_method *method,
+ pj_int32_t expires,
+ pjsip_tx_data **p_tdata);
+
+
+/**
+ * Accept the incoming subscription request by sending 2xx response to
+ * incoming SUBSCRIBE request.
+ *
+ * @param sub Server subscription instance.
+ * @param rdata The incoming subscription request message.
+ * @param st_code Status code, which MUST be final response.
+ * @param hdr_list Optional list of headers to be added in the response.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pjsip_hdr *hdr_list );
+
+
+/**
+ * For notifier, create NOTIFY request to subscriber, and set the state
+ * of the subscription.
+ *
+ * @param sub The server subscription (notifier) instance.
+ * @param state New state to set.
+ * @param state_str The state string name, if state contains value other
+ * than active, pending, or terminated. Otherwise this
+ * argument is ignored.
+ * @param reason Specify reason if new state is terminated, otherwise
+ * put NULL.
+ * @param p_tdata Pointer to receive request message.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pjsip_tx_data **p_tdata);
+
+
+/**
+ * For notifier, create a NOTIFY request that reflects current subscription
+ * status.
+ *
+ * @param sub The server subscription instance.
+ * @param p_tdata Pointer to receive the request messge.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
+ pjsip_tx_data **p_tdata );
+
+
+
+/**
+ * Send request message.
+ *
+ * @param sub The event subscription object.
+ * @param tdata Request message to be send.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
+ pjsip_tx_data *tdata);
+
+
+
+/**
+ * Get the event subscription instance in the transaction.
+ *
+ * @param tsx The transaction.
+ *
+ * @return The event subscription instance registered in the
+ * transaction, if any.
+ */
+PJ_DECL(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx);
+
+
+/**
+ * Set event subscription's module data.
+ *
+ * @param sub The event subscription.
+ * @param index The module id.
+ * @param data Arbitrary data.
+ */
+PJ_DECL(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
+ void *data );
+
+
+/**
+ * Get event subscription's module data.
+ *
+ * @param sub The event subscription.
+ * @param mod_id The module id.
+ *
+ * @return Data previously set at the specified id.
+ */
+PJ_DECL(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id );
+
+
+
+PJ_END_DECL
+
+/**
+ * @}
+ */
+
+#endif /* __PJSIP_SIMPLE_EVSUB_H__ */
diff --git a/pjsip/include/pjsip-simple/event_notify_msg.h b/pjsip/include/pjsip-simple/evsub_msg.h
index 76b7d35c..4f4a9b92 100644
--- a/pjsip/include/pjsip-simple/event_notify_msg.h
+++ b/pjsip/include/pjsip-simple/evsub_msg.h
@@ -41,10 +41,10 @@ PJ_BEGIN_DECL
*/
typedef struct pjsip_event_hdr
{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr)
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr);
pj_str_t event_type; /**< Event name. */
pj_str_t id_param; /**< Optional event ID parameter. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
+ pjsip_param other_param; /**< Other parameter. */
} pjsip_event_hdr;
/**
@@ -60,12 +60,7 @@ PJ_DECL(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool);
/**
* This structure describes Allow-Events header.
*/
-typedef struct pjsip_allow_events_hdr
-{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr)
- int event_cnt; /**< Number of event names. */
- pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */
-} pjsip_allow_events_hdr;
+typedef pjsip_generic_array_hdr pjsip_allow_events_hdr;
/**
@@ -83,12 +78,12 @@ PJ_DECL(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool);
*/
typedef struct pjsip_sub_state_hdr
{
- PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr)
+ PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr);
pj_str_t sub_state; /**< Subscription state. */
pj_str_t reason_param; /**< Optional termination reason. */
int expires_param; /**< Expires param, or -1. */
int retry_after; /**< Retry after param, or -1. */
- pj_str_t other_param; /**< Other parameter, concatenated together. */
+ pjsip_param other_param; /**< Other parameters. */
} pjsip_sub_state_hdr;
/**
@@ -103,7 +98,7 @@ PJ_DECL(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool);
/**
* Initialize parser for event notify module.
*/
-PJ_DEF(void) pjsip_event_notify_init_parser(void);
+PJ_DEF(void) pjsip_evsub_init_parser(void);
PJ_END_DECL
diff --git a/pjsip/include/pjsip-simple/messaging.h b/pjsip/include/pjsip-simple/messaging.h
deleted file mode 100644
index 742d739c..00000000
--- a/pjsip/include/pjsip-simple/messaging.h
+++ /dev/null
@@ -1,268 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- */
-#ifndef __PJSIP_SIMPLE_MESSAGING_H__
-#define __PJSIP_SIMPLE_MESSAGING_H__
-
-/**
- * @file messaging.h
- * @brief Instant Messaging Extension (RFC 3428)
- */
-
-#include <pjsip/sip_msg.h>
-
-PJ_BEGIN_DECL
-
-/**
- * @defgroup PJSIP_MESSAGING SIP Instant Messaging (RFC 3428) Module
- * @ingroup PJSIP_SIMPLE
- * @{
- *
- * This module provides the implementation of SIP Extension for Instant
- * Messaging (RFC 3428). It extends PJSIP by supporting MESSAGE method.
- *
- * The RFC 3428 doesn't provide any means of dialog for the purpose of sending/
- * receiving instant messaging. IM with SIP is basicly sessionless, which means
- * that there is absolutely no correlation between IM messages sent or received
- * by a host. Any correlation between IM messages is only perceivable by
- * user, phsychologically.
- *
- * However, the RFC doesn't prohibit sending IM within a dialog (presumably
- * using the same Call-ID and CSeq namespace), although it prohibits creating
- * a dialog specificly for creating IM session.
- *
- * The implementation here is modeled to support both ways of sending IM msgs,
- * i.e. sending IM message individually and sending IM message within a dialog.
- * Although IM message can be associated with a dialog, this implementation of
- * IM module is completely independent of the User Agent library in PJSIP. Yes,
- * that's what is called modularity, and it demonstrates the clearness
- * of PJSIP design (the last sentence is of course marketing crap :)).
- *
- * To send an IM message as part of dialog, application would first create the
- * message using #pjsip_messaging_create_msg, using dialog's Call-ID, CSeq,
- * From, and To header, then send the message using #pjsip_dlg_send_msg instead
- * of #pjsip_messaging_send_msg.
- *
- * To send IM messages individually, application has two options. The first is
- * to create the request with #pjsip_messaging_create_msg then send it with
- * #pjsip_messaging_send_msg. But this way, application has to pre-construct
- * From and To header first, which is not too convenient.
- *
- * The second option (to send IM messages not associated with a dialog) is to
- * first create an 'IM session'. An 'IM session' here is not a SIP dialog, as
- * it doesn't have Contact header etc. An 'IM session' here is just a local
- * state to cache most of IM headers, for convenience and optimization. Appl
- * creates an IM session with #pjsip_messaging_create_session, and destroy
- * the session with #pjsip_messaging_destroy_session. To create an outgoing
- * IM message, application would call #pjsip_messaging_session_create_msg,
- * and to send the message it would use #pjsip_messaging_send_msg.
- *
- * Message authorization is handled by application, as usual, by inserting a
- * proper WWW-Authenticate or Proxy-Authenticate header before sending the
- * message.
- *
- * And the last bit, to handle incoing IM messages.
- *
- * To handle incoming IM messages, application would register a global callback
- * to be called when incoming messages arrive, by registering with function
- * #pjsip_messaging_set_incoming_callback. This will be the global callback
- * for all incoming IM messages. Although the message was sent as part of
- * a dialog, it would still come here. And as long as the request has proper
- * identification (Call-ID, From/To tag), the dialog will be aware of the
- * request and update it's state (remote CSeq) accordingly.
- */
-
-
-
-/**
- * Typedef for callback to be called when outgoing message has been sent
- * and a final response has been received.
- */
-typedef void (*pjsip_messaging_cb)(void *token, int status_code);
-
-/**
- * Typedef for callback to receive incoming message.
- *
- * @param rdata Incoming message data.
- *
- * @return The status code to be returned back to original sender.
- * Application must return a final status code upon returning
- * from this function, or otherwise the stack will respond
- * with error.
- */
-typedef int (*pjsip_on_new_msg_cb)(pjsip_rx_data *rdata);
-
-/**
- * Opaque data type for instant messaging session.
- */
-typedef struct pjsip_messaging_session pjsip_messaging_session;
-
-/**
- * Get the messaging module.
- *
- * @return SIP module.
- */
-PJ_DECL(pjsip_module*) pjsip_messaging_get_module();
-
-/**
- * Set the global callback to be called when incoming message is received.
- *
- * @param cb The callback to be called when incoming message is received.
- *
- * @return The previous callback.
- */
-PJ_DECL(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb);
-
-
-/**
- * Create an instant message transmit data buffer using the specified arguments.
- * The returned transmit data buffers will have it's reference counter set
- * to 1, and when application send the buffer, the send function will decrement
- * the reference counter. When the reference counter reach zero, the buffer
- * will be deleted. As long as the function does not increment the buffer's
- * reference counter between creating and sending the request, the buffer
- * will always be deleted and no memory leak will occur.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from The "From" header, which content will be copied to request.
- * If the "From" header doesn't have a tag parameter, the
- * function will generate one.
- * @param to The "To" header, which content will be copied to request.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *from,
- const pjsip_to_hdr *to,
- const pjsip_cid_hdr *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Create instant message, by specifying URL string for both From and To header.
- *
- * @param endpt Endpoint instance.
- * @param target Target URL.
- * @param from URL of the sender.
- * @param to URL of the recipient.
- * @param call_id Optionally specify Call-ID, or put NULL to make this
- * function generate a unique Call-ID automatically.
- * @param cseq Optionally specify CSeq, or put -1 to make this function
- * generate a random CSeq.
- * @param text Optionally specify "text/plain" message body, or put NULL
- * if application wants to put body other than "text/plain"
- * manually.
- *
- * @return SIP transmit data buffer, which reference count has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*) pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *call_id,
- int cseq,
- const pj_str_t *text);
-
-/**
- * Send the instant message transmit buffer and attach a callback to be called
- * when the request has received a final response. This function will decrement
- * the transmit buffer's reference counter, and when the reference counter
- * reach zero, the buffer will be deleted. As long as the function does not
- * increment the buffer's reference counter between creating the request and
- * calling this function, the buffer will always be deleted regardless whether
- * the sending was failed or succeeded.
- *
- * @param endpt Endpoint instance.
- * @param tdata Transmit data buffer.
- * @param token Token to be associated with the SIP transaction which sends
- * this request.
- * @param cb The callback to be called when the SIP request has received
- * a final response from destination.
- *
- * @return Zero if the transaction was started successfully. Note that
- * this doesn't mean the request has been received successfully
- * by remote recipient.
- */
-PJ_DECL(pj_status_t) pjsip_messaging_send_msg( pjsip_endpoint *endpt,
- pjsip_tx_data *tdata,
- void *token,
- pjsip_messaging_cb cb );
-
-/**
- * Create an instant messaging session, which can conveniently be used to send
- * multiple instant messages to the same recipient.
- *
- * @param endpt Endpoint instance.
- * @param from URI of sender. The function will add a unique tag parameter
- * to this URI in the From header.
- * @param to URI of recipient.
- *
- * @return Messaging session.
- */
-PJ_DECL(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to );
-
-/**
- * Create an instant message using instant messaging session, and optionally
- * attach a text message.
- *
- * @param ses The instant messaging session.
- * @param text Optional "text/plain" message to be attached as the
- * message body. If this parameter is NULL, then no message
- * body will be created, and application can attach any
- * type of message body before the request is sent.
- *
- * @return SIP transmit data buffer, which reference counter has been
- * set to 1.
- */
-PJ_DECL(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses,
- const pj_str_t *text );
-
-/**
- * Destroy an instant messaging session.
- *
- * @param ses The instant messaging session.
- *
- * @return Zero on successfull.
- */
-PJ_DECL(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses );
-
-/**
- * @}
- */
-
-PJ_END_DECL
-
-#endif
diff --git a/pjsip/include/pjsip-simple/pidf.h b/pjsip/include/pjsip-simple/pidf.h
index b9dd4509..1b57c5d2 100644
--- a/pjsip/include/pjsip-simple/pidf.h
+++ b/pjsip/include/pjsip-simple/pidf.h
@@ -23,8 +23,8 @@
* @file pidf.h
* @brief PIDF/Presence Information Data Format (RFC 3863)
*/
-#include <pj/types.h>
-#include <pj/xml.h>
+#include <pjsip-simple/types.h>
+#include <pjlib-util/xml.h>
PJ_BEGIN_DECL
diff --git a/pjsip/include/pjsip-simple/presence.h b/pjsip/include/pjsip-simple/presence.h
index 180ac4d1..e02498a7 100644
--- a/pjsip/include/pjsip-simple/presence.h
+++ b/pjsip/include/pjsip-simple/presence.h
@@ -23,9 +23,9 @@
* @file presence.h
* @brief SIP Extension for Presence (RFC 3856)
*/
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/xpidf.h>
+#include <pjsip-simple/evsub.h>
+#include <pjsip-simple/pidf.h>
+#include <pjsip-simple/xpidf.h>
PJ_BEGIN_DECL
@@ -38,185 +38,206 @@ PJ_BEGIN_DECL
*
* This module contains the implementation of SIP Presence Extension as
* described in RFC 3856. It uses the SIP Event Notification framework
- * (event_notify.h) and extends the framework by implementing "presence"
+ * (evsub.h) and extends the framework by implementing "presence"
* event package.
*/
+
+
/**
- * Presence message body type.
+ * Initialize the presence module and register it as endpoint module and
+ * package to the event subscription module.
+ *
+ * @param endpt The endpoint instance.
+ * @param mod_evsub The event subscription module instance.
+ *
+ * @return PJ_SUCCESS if the module is successfully
+ * initialized and registered to both endpoint
+ * and the event subscription module.
*/
-typedef enum pjsip_pres_type
-{
- PJSIP_PRES_TYPE_PIDF,
- PJSIP_PRES_TYPE_XPIDF,
-} pjsip_pres_type;
+PJ_DECL(pj_status_t) pjsip_pres_init_module(pjsip_endpoint *endpt,
+ pjsip_module *mod_evsub);
+
/**
- * This structure describe a presentity, for both subscriber and notifier.
+ * Get the presence module instance.
+ *
+ * @return The presence module instance.
*/
-typedef struct pjsip_presentity
-{
- pjsip_event_sub *sub; /**< Event subscribtion record. */
- pjsip_pres_type pres_type; /**< Presentity type. */
- pjsip_msg_body *uas_body; /**< Message body (UAS only). */
- union {
- pjpidf_pres *pidf;
- pjxpidf_pres *xpidf;
- } uas_data; /**< UAS data. */
- pj_str_t timestamp; /**< Time of last update. */
- void *user_data; /**< Application data. */
-} pjsip_presentity;
+PJ_DECL(pjsip_module*) pjsip_pres_instance(void);
+
+#define PJSIP_PRES_STATUS_MAX_INFO 8
/**
- * This structure describe callback that is registered to receive notification
- * from the presence module.
+ * This structure describes presence status of a presentity.
*/
-typedef struct pjsip_presence_cb
+struct pjsip_pres_status
{
- /**
- * This callback is first called when the module receives incoming
- * SUBSCRIBE request to determine whether application wants to accept
- * the request. If it does, then on_presence_request will be called.
- *
- * @param rdata The received message.
- * @return Application should return 2xx to accept the request,
- * or failure status (>=300) to reject the request.
- */
- void (*accept_presence)(pjsip_rx_data *rdata, int *status);
+ unsigned info_cnt; /**< Number of info in the status. */
+ struct {
- /**
- * This callback is called when the module receive the first presence
- * subscription request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- * @param timeout Timeout to be set for incoming request. Otherwise
- * app can just leave this and accept the default.
- */
- void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata,
- int *timeout);
+ pj_bool_t basic_open; /**< Basic status/availability. */
+ pj_str_t id; /**< Tuple id. */
+ pj_str_t contact; /**< Optional contact address. */
- /**
- * This callback is called when the module received subscription refresh
- * request.
- *
- * @param pres The presence descriptor.
- * @param rdata The incoming request.
- */
- void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata);
+ } info[PJSIP_PRES_STATUS_MAX_INFO]; /**< Array of info. */
- /**
- * This callback is called when the module receives incoming NOTIFY
- * request.
- *
- * @param pres The presence descriptor.
- * @param open The latest status of the presentity.
- */
- void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open);
+ pj_bool_t _is_valid; /**< Internal flag. */
+};
- /**
- * This callback is called when the subscription has terminated.
- *
- * @param sub The subscription instance.
- * @param reason The termination reason.
- */
- void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason);
-} pjsip_presence_cb;
+/**
+ * @see pjsip_pres_status
+ */
+typedef struct pjsip_pres_status pjsip_pres_status;
/**
- * Initialize the presence module and register callback.
+ * Create presence client subscription session.
*
- * @param cb Callback structure.
+ * @param dlg The underlying dialog to use.
+ * @param user_cb Pointer to callbacks to receive presence subscription
+ * events.
+ * @param p_evsub Pointer to receive the presence subscription
+ * session.
+ *
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb);
+PJ_DECL(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_evsub **p_evsub );
/**
- * Create to presence subscription of a presentity URL.
+ * Create presence server subscription session.
*
- * @param endpt Endpoint instance.
- * @param local_url Local URL.
- * @param remote_url Remote URL which the presence is being subscribed.
- * @param expires The expiration.
- * @param user_data User data to attach to presence subscription.
+ * @param dlg The underlying dialog to use.
+ * @param user_cb Pointer to callbacks to receive presence subscription
+ * events.
+ * @param rdata The incoming SUBSCRIBE request that creates the event
+ * subscription.
+ * @param p_evsub Pointer to receive the presence subscription
+ * session.
*
- * @return The presence structure if successfull, or NULL if
- * failed.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data );
+PJ_DECL(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_rx_data *rdata,
+ pjsip_evsub **p_evsub );
+
/**
- * Set credentials to be used by this presentity for outgoing requests.
+ * Call this function to create request to initiate presence subscription, to
+ * refresh subcription, or to request subscription termination.
*
- * @param pres Presentity instance.
- * @param count Number of credentials in the array.
- * @param cred Array of credentials.
+ * @param sub Client subscription instance.
+ * @param expires Subscription expiration. If the value is set to zero,
+ * this will request unsubscription.
+ * @param p_tdata Pointer to receive the request.
*
- * @return Zero on success.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[]);
+PJ_DECL(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
+ pj_int32_t expires,
+ pjsip_tx_data **p_tdata);
+
+
/**
- * Set route set for outgoing requests.
+ * Accept the incoming subscription request by sending 2xx response to
+ * incoming SUBSCRIBE request.
*
- * @param pres Presentity instance.
- * @param route_set List of route headers.
+ * @param sub Server subscription instance.
+ * @param rdata The incoming subscription request message.
+ * @param st_code Status code, which MUST be final response.
+ * @param hdr_list Optional list of headers to be added in the response.
*
- * @return Zero on success.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr );
+PJ_DECL(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pjsip_hdr *hdr_list );
+
+
+
/**
- * Send SUBSCRIBE request for the specified presentity.
+ * For notifier, create NOTIFY request to subscriber, and set the state
+ * of the subscription. Application MUST set the presence status to the
+ * appropriate state (by calling #pjsip_pres_set_status()) before calling
+ * this function.
+ *
+ * @param sub The server subscription (notifier) instance.
+ * @param state New state to set.
+ * @param state_str The state string name, if state contains value other
+ * than active, pending, or terminated. Otherwise this
+ * argument is ignored.
+ * @param reason Specify reason if new state is terminated, otherwise
+ * put NULL.
+ * @param p_tdata Pointer to receive the request.
+ *
+ * @return PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pjsip_tx_data **p_tdata);
+
+
+/**
+ * Create NOTIFY request to reflect current subscription status.
*
- * @param pres The presentity instance.
+ * @param sub Server subscription object.
+ * @param p_tdata Pointer to receive request.
*
- * @return Zero on success.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres );
+PJ_DECL(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
+ pjsip_tx_data **p_tdata );
+
+
/**
- * Ceased the presence subscription.
+ * Send request.
*
- * @param pres The presence structure.
- *
- * @return Zero on success.
+ * @param sub The subscription object.
+ * @param tdata Request message to be sent.
+ *
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres );
+PJ_DECL(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
+ pjsip_tx_data *tdata );
+
/**
- * Notify subscriber about change in local status.
+ * Get the presence status. Client normally would call this function
+ * after receiving NOTIFY request from server.
*
- * @param pres The presence structure.
- * @param state Set the state of the subscription.
- * @param open Set the presence status (open or closed).
+ * @param sub The client or server subscription.
+ * @param status The structure to receive presence status.
*
- * @return Zero if a NOTIFY request can be sent.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t open );
+PJ_DECL(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
+ pjsip_pres_status *status );
+
/**
- * Destroy presence structure and the underlying subscription.
+ * Set the presence status. This operation is only valid for server
+ * subscription. After calling this function, application would need to
+ * send NOTIFY request to client.
*
- * @param pres The presence structure.
+ * @param sub The server subscription.
+ * @param status Status to be set.
*
- * @return Zero if the subscription was destroyed, or one if
- * the subscription can not be destroyed immediately
- * and will be destroyed later, or -1 if failed.
+ * @return PJ_SUCCESS on success.
*/
-PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres );
+PJ_DECL(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
+ const pjsip_pres_status *status );
/**
diff --git a/pjsip/include/pjsip-simple/types.h b/pjsip/include/pjsip-simple/types.h
new file mode 100644
index 00000000..e202a2b9
--- /dev/null
+++ b/pjsip/include/pjsip-simple/types.h
@@ -0,0 +1,30 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#ifndef __PJSIP_SIMPLE_TYPES_H__
+#define __PJSIP_SIMPLE_TYPES_H__
+
+#include <pjsip/sip_types.h>
+
+
+#define PJSIP_EVSUB_POOL_LEN 4000
+#define PJSIP_EVSUB_POOL_INC 4000
+
+
+#endif /* __PJSIP_SIMPLE_TYPES_H__ */
+
diff --git a/pjsip/include/pjsip-simple/xpidf.h b/pjsip/include/pjsip-simple/xpidf.h
index 59d3c398..c5736c54 100644
--- a/pjsip/include/pjsip-simple/xpidf.h
+++ b/pjsip/include/pjsip-simple/xpidf.h
@@ -23,8 +23,8 @@
* @file xpidf.h
* @brief XPIDF/Presence Information Data Format
*/
-#include <pj/types.h>
-#include <pj/xml.h>
+#include <pjsip-simple/types.h>
+#include <pjlib-util/xml.h>
PJ_BEGIN_DECL
diff --git a/pjsip/include/pjsip/sip_config.h b/pjsip/include/pjsip/sip_config.h
index ec4a4aad..48d86c63 100644
--- a/pjsip/include/pjsip/sip_config.h
+++ b/pjsip/include/pjsip/sip_config.h
@@ -33,8 +33,8 @@
#define PJSIP_POOL_RDATA_INC 4000
#define PJSIP_POOL_LEN_TRANSPORT 512
#define PJSIP_POOL_INC_TRANSPORT 512
-#define PJSIP_POOL_LEN_TDATA 2500
-#define PJSIP_POOL_INC_TDATA 512
+#define PJSIP_POOL_LEN_TDATA 4000
+#define PJSIP_POOL_INC_TDATA 4000
#define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT)
#define PJSIP_POOL_INC_UA 0
#define PJSIP_TRANSPORT_CLOSE_TIMEOUT 30
@@ -79,7 +79,7 @@
#define PJSIP_MAX_TIMED_OUT_ENTRIES 10
/* Module related constants. */
-#define PJSIP_MAX_MODULE 8
+#define PJSIP_MAX_MODULE 16
/* Maximum header types. */
#define PJSIP_MAX_HEADER_TYPES 64
diff --git a/pjsip/include/pjsip/sip_errno.h b/pjsip/include/pjsip/sip_errno.h
index d5000955..70cd884e 100644
--- a/pjsip/include/pjsip/sip_errno.h
+++ b/pjsip/include/pjsip/sip_errno.h
@@ -207,6 +207,11 @@ PJ_BEGIN_DECL
* Transaction has just been destroyed.
*/
#define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 70) /* 171070 */
+/**
+ * @hideinitializer
+ * No transaction.
+ */
+#define PJSIP_ENOTSX (PJSIP_ERRNO_START_PJSIP + 71) /* 171071 */
/************************************************************
diff --git a/pjsip/include/pjsip/sip_msg.h b/pjsip/include/pjsip/sip_msg.h
index a6a1d656..0cef6a36 100644
--- a/pjsip/include/pjsip/sip_msg.h
+++ b/pjsip/include/pjsip/sip_msg.h
@@ -411,7 +411,7 @@ typedef enum pjsip_status_code
PJSIP_SC_BUSY_HERE = 486,
PJSIP_SC_REQUEST_TERMINATED = 487,
PJSIP_SC_NOT_ACCEPTABLE_HERE = 488,
- PJSIP_SC_UNKNOWN_EVENT = 489,
+ PJSIP_SC_BAD_EVENT = 489,
PJSIP_SC_REQUEST_UPDATED = 490,
PJSIP_SC_REQUEST_PENDING = 491,
PJSIP_SC_UNDECIPHERABLE = 493,
@@ -498,7 +498,7 @@ typedef struct pjsip_media_type
* When application needs to attach message body to outgoing SIP message, it
* must fill in all members of this structure.
*/
-typedef struct pjsip_msg_body
+struct pjsip_msg_body
{
/** MIME content type.
* For incoming messages, the parser will fill in this member with the
@@ -565,7 +565,7 @@ typedef struct pjsip_msg_body
*/
void* (*clone_data)(pj_pool_t *pool, const void *data, unsigned len);
-} pjsip_msg_body;
+};
/**
* General purpose function to textual data in a SIP body. Attach this function
diff --git a/pjsip/include/pjsip/sip_types.h b/pjsip/include/pjsip/sip_types.h
index 8ea6c414..53035529 100644
--- a/pjsip/include/pjsip/sip_types.h
+++ b/pjsip/include/pjsip/sip_types.h
@@ -92,6 +92,11 @@ typedef struct pjsip_rx_data pjsip_rx_data;
typedef struct pjsip_msg pjsip_msg;
/**
+ * Forward declaration for message body (sip_msg.h).
+ */
+typedef struct pjsip_msg_body pjsip_msg_body;
+
+/**
* Forward declaration for header field (sip_msg.h).
*/
typedef struct pjsip_hdr pjsip_hdr;
@@ -143,8 +148,14 @@ typedef enum pjsip_dialog_state pjsip_dialog_state;
*/
typedef enum pjsip_role_e
{
- PJSIP_ROLE_UAC, /**< Transaction role is UAC. */
- PJSIP_ROLE_UAS, /**< Transaction role is UAS. */
+ PJSIP_ROLE_UAC, /**< Role is UAC. */
+ PJSIP_ROLE_UAS, /**< Role is UAS. */
+
+ /* Alias: */
+
+ PJSIP_UAC_ROLE = PJSIP_ROLE_UAC, /**< Role is UAC. */
+ PJSIP_UAS_ROLE = PJSIP_ROLE_UAS, /**< Role is UAS. */
+
} pjsip_role_e;
diff --git a/pjsip/include/pjsip_simple.h b/pjsip/include/pjsip_simple.h
index 29034e63..a8c955ca 100644
--- a/pjsip/include/pjsip_simple.h
+++ b/pjsip/include/pjsip_simple.h
@@ -33,9 +33,9 @@
#ifndef __PJSIP_SIMPLE_H__
#define __PJSIP_SIMPLE_H__
-#include <pjsip_simple/messaging.h>
-#include <pjsip_simple/event_notify.h>
-#include <pjsip_simple/pidf.h>
-#include <pjsip_simple/presence.h>
+#include <pjsip-simple/evsub.h>
+#include <pjsip-simple/presence.h>
+#include <pjsip-simple/pidf.h>
+#include <pjsip-simple/xpidf.h>
#endif /* __PJSIP_SIMPLE_H__ */
diff --git a/pjsip/src/pjsip-simple/event_notify.c b/pjsip/src/pjsip-simple/event_notify.c
deleted file mode 100644
index 51fe7694..00000000
--- a/pjsip/src/pjsip-simple/event_notify.c
+++ /dev/null
@@ -1,1644 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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_simple/event_notify.h>
-#include <pjsip/sip_msg.h>
-#include <pjsip/sip_util.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pj/pool.h>
-#include <pj/timer.h>
-#include <pj/string.h>
-#include <pj/hash.h>
-#include <pj/os.h>
-#include <pj/except.h>
-#include <pj/log.h>
-#include <pj/guid.h>
-
-#define THIS_FILE "event_sub"
-
-/* String names for state.
- * The names here should be compliant with sub_state names in RFC3265.
- */
-static const pj_str_t state[] = {
- { "null", 4 },
- { "active", 6 },
- { "pending", 7 },
- { "terminated", 10 },
- { "unknown", 7 }
-};
-
-/* Timer IDs */
-#define TIMER_ID_REFRESH 1
-#define TIMER_ID_UAS_EXPIRY 2
-
-/* Static configuration. */
-#define SECONDS_BEFORE_EXPIRY 10
-#define MGR_POOL_SIZE 512
-#define MGR_POOL_INC 0
-#define SUB_POOL_SIZE 2048
-#define SUB_POOL_INC 0
-#define HASH_TABLE_SIZE 32
-
-/* Static vars. */
-static int mod_id;
-static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}};
-static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}};
-
-typedef struct package
-{
- PJ_DECL_LIST_MEMBER(struct package)
- pj_str_t event;
- int accept_cnt;
- pj_str_t *accept;
- pjsip_event_sub_pkg_cb cb;
-} package;
-
-/* Event subscription manager singleton instance. */
-static struct pjsip_event_sub_mgr
-{
- pj_pool_t *pool;
- pj_hash_table_t *ht;
- pjsip_endpoint *endpt;
- pj_mutex_t *mutex;
- pjsip_allow_events_hdr *allow_events;
- package pkg_list;
-} mgr;
-
-/* Fordward declarations for static functions. */
-static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t);
-static pj_status_t mod_deinit(pjsip_module*);
-static void tsx_handler(pjsip_module*, pjsip_event*);
-static pjsip_event_sub *find_sub(pjsip_rx_data *);
-static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*);
-static void on_subscribe_response(void *, pjsip_event*);
-static void on_notify_request(pjsip_transaction *, pjsip_rx_data*);
-static void on_notify_response(void *, pjsip_event *);
-static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*);
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub );
-
-/* Module descriptor. */
-static pjsip_module event_sub_module =
-{
- {"EventSub", 8}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- &mgr, /* User data. */
- 2, /* Number of methods supported . */
- { &SUBSCRIBE, &NOTIFY }, /* Array of methods */
- &mod_init, /* init_module() */
- NULL, /* start_module() */
- &mod_deinit, /* deinit_module() */
- &tsx_handler, /* tsx_handler() */
-};
-
-/*
- * Module initialization.
- * This will be called by endpoint when it initializes all modules.
- */
-static pj_status_t mod_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- pj_pool_t *pool;
-
- pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC);
- if (!pool)
- return -1;
-
- /* Manager initialization: create hash table and mutex. */
- mgr.pool = pool;
- mgr.endpt = endpt;
- mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE);
- if (!mgr.ht)
- return -1;
-
- mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE);
- if (!mgr.mutex)
- return -1;
-
- /* Attach manager to module. */
- mod->mod_data = &mgr;
-
- /* Init package list. */
- pj_list_init(&mgr.pkg_list);
-
- /* Init Allow-Events header. */
- mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool);
-
- /* Save the module ID. */
- mod_id = id;
-
- pjsip_event_notify_init_parser();
- return 0;
-}
-
-/*
- * Module deinitialization.
- * Called by endpoint.
- */
-static pj_status_t mod_deinit( struct pjsip_module *mod )
-{
- pj_mutex_lock(mgr.mutex);
- pj_mutex_destroy(mgr.mutex);
- pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool);
- return 0;
-}
-
-/*
- * This public function is called by application to register callback.
- * In exchange, the instance of the module is returned.
- */
-PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void)
-{
- return &event_sub_module;
-}
-
-/*
- * Register event package.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event,
- int accept_cnt,
- const pj_str_t accept[],
- const pjsip_event_sub_pkg_cb *cb )
-{
- package *pkg;
- int i;
-
- pj_mutex_lock(mgr.mutex);
-
- /* Create and register new package. */
- pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg));
- pj_strdup(mgr.pool, &pkg->event, event);
- pj_list_insert_before(&mgr.pkg_list, pkg);
-
- /* Save Accept specification. */
- pkg->accept_cnt = accept_cnt;
- pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t));
- for (i=0; i<accept_cnt; ++i) {
- pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]);
- }
-
- /* Copy callback. */
- pj_memcpy(&pkg->cb, cb, sizeof(*cb));
-
- /* Update Allow-Events header. */
- pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS);
- mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event;
-
- pj_mutex_unlock(mgr.mutex);
- return 0;
-}
-
-/*
- * Create subscription key (for hash table).
- */
-static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool,
- pjsip_role_e role,
- const pj_str_t *call_id, const pj_str_t *from_tag)
-{
- char *p;
-
- p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3);
- *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C');
- *p++ = '$';
- pj_memcpy(p, call_id->ptr, call_id->slen);
- p += call_id->slen;
- *p++ = '$';
- pj_memcpy(p, from_tag->ptr, from_tag->slen);
- p += from_tag->slen;
-
- key->slen = p - key->ptr;
-}
-
-
-/*
- * Create UAC subscription.
- */
-PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt,
- const pj_str_t *from,
- const pj_str_t *to,
- const pj_str_t *event,
- int expires,
- int accept_cnt,
- const pj_str_t accept[],
- void *user_data,
- const pjsip_event_sub_cb *cb)
-{
- pjsip_tx_data *tdata;
- pj_pool_t *pool;
- const pjsip_hdr *hdr;
- pjsip_event_sub *sub;
- PJ_USE_EXCEPTION;
-
- PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s",
- event->slen, event->ptr, to->slen, to->ptr));
-
- /* Create pool for the event subscription. */
- pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- return NULL;
- }
-
- /* Init subscription. */
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->endpt = endpt;
- sub->role = PJSIP_ROLE_UAC;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- sub->user_data = user_data;
- sub->timer.id = 0;
- sub->default_interval = expires;
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /* The easiest way to parse the parameters is to create a dummy request! */
- tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from,
- NULL, -1, NULL);
- if (!tdata) {
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- /*
- * Duplicate headers in the request to our structure.
- */
- PJ_TRY {
- int i;
-
- /* From */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL);
- pj_assert(hdr != NULL);
- sub->from = pjsip_hdr_clone(pool, hdr);
-
- /* To */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL);
- pj_assert(hdr != NULL);
- sub->to = pjsip_hdr_clone(pool, hdr);
-
- /* Contact. */
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
-
- /* Call-ID */
- hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL);
- pj_assert(hdr != NULL);
- sub->call_id = pjsip_hdr_clone(pool, hdr);
-
- /* CSeq */
- sub->cseq = pj_rand() % 0xFFFF;
-
- /* Event. */
- sub->event = pjsip_event_hdr_create(sub->pool);
- pj_strdup(pool, &sub->event->event_type, event);
-
- /* Expires. */
- sub->uac_expires = pjsip_expires_hdr_create(pool);
- sub->uac_expires->ivalue = expires;
-
- /* Accept. */
- sub->local_accept = pjsip_accept_hdr_create(pool);
- for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) {
- sub->local_accept->count++;
- pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]);
- }
-
- /* Register to hash table. */
- create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC,
- &sub->call_id->id, &sub->from->tag);
- pj_mutex_lock( mgr.mutex );
- pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock( mgr.mutex );
-
- }
- PJ_CATCH_ANY {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init",
- sub, state[sub->state].ptr, PJ_GET_EXCEPTION()));
-
- pjsip_tx_data_dec_ref(tdata);
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(endpt, sub->pool);
- return NULL;
- }
- PJ_END;
-
- /* All set, delete temporary transmit data as we don't need it. */
- pjsip_tx_data_dec_ref(tdata);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s",
- sub, state[sub->state].ptr,
- to->slen, to->ptr, event->slen, event->ptr));
-
- return sub;
-}
-
-/*
- * Set credentials.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub,
- int count,
- const pjsip_cred_info cred[])
-{
- pj_mutex_lock(sub->mutex);
- if (count > 0) {
- sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info));
- pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info));
- }
- sub->cred_cnt = count;
- pj_mutex_unlock(sub->mutex);
- return 0;
-}
-
-/*
- * Set route-set.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub,
- const pjsip_route_hdr *route_set )
-{
- const pjsip_route_hdr *hdr;
-
- pj_mutex_lock(sub->mutex);
-
- /* Clear existing route set. */
- pj_list_init(&sub->route_set);
-
- /* Duplicate route headers. */
- hdr = route_set->next;
- while (hdr != route_set) {
- pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr);
- pj_list_insert_before(&sub->route_set, new_hdr);
- hdr = hdr->next;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- return 0;
-}
-
-/*
- * Send subscribe request.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub )
-{
- pj_status_t status;
-
- pj_mutex_lock(sub->mutex);
- status = send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-
- return status;
-}
-
-/*
- * Destroy subscription.
- * If there are pending transactions, then this will just set the flag.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub)
-{
- pj_assert(sub != NULL);
- if (sub == NULL)
- return -1;
-
- /* Application must terminate the subscription first. */
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(mgr.mutex);
- pj_mutex_lock(sub->mutex);
-
- /* Set delete flag. */
- sub->delete_flag = 1;
-
- /* Unregister timer, if any. */
- if (sub->timer.id != 0) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- if (sub->pending_tsx > 0) {
- pj_mutex_unlock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later",
- sub, state[sub->state].ptr,
- sub->pending_tsx));
- return 1;
- }
-
- /* Unregister from hash table. */
- pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL);
-
- /* Destroy. */
- pj_mutex_destroy(sub->mutex);
- pjsip_endpt_destroy_pool(sub->endpt, sub->pool);
-
- pj_mutex_unlock(mgr.mutex);
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub));
- return 0;
-}
-
-/* Change state. */
-static void sub_set_state( pjsip_event_sub *sub, int new_state)
-{
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s",
- sub, state[sub->state].ptr, state[new_state].ptr));
- sub->state = new_state;
- sub->state_str = state[new_state];
-}
-
-/*
- * Refresh subscription.
- */
-static pj_status_t send_sub_refresh( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- pj_status_t status;
- const pjsip_route_hdr *route;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->role != PJSIP_ROLE_UAC ||
- sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- return -1;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription",
- sub, state[sub->state].ptr));
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!",
- sub, state[sub->state].ptr));
- return -1;
- }
-
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept));
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Send */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- } else {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Stop subscription.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub )
-{
- pjsip_tx_data *tdata;
- const pjsip_route_hdr *route;
- pj_status_t status;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...",
- sub, state[sub->state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Kill refresh timer, if any. */
- if (sub->timer.id != 0) {
- sub->timer.id = 0;
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- }
-
- /* Create request. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &SUBSCRIBE,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
-
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add headers to request. */
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
- sub->uac_expires->ivalue = 0;
- pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires));
-
- /* Add authentication. */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Prevent timer from refreshing itself. */
- sub->default_interval = 0;
-
- /* Set state. */
- sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED );
-
- /* Send the request. */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- sub->pending_tsx++;
- }
-
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!",
- sub, state[sub->state].ptr));
- }
-
- return status;
-}
-
-/*
- * Send notify.
- */
-PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub,
- pjsip_event_sub_state new_state,
- const pj_str_t *reason,
- pjsip_msg_body *body)
-{
- pjsip_tx_data *tdata;
- pjsip_sub_state_hdr *ss_hdr;
- const pjsip_route_hdr *route;
- pj_time_val now;
- pj_status_t status;
- pjsip_event_sub_state old_state = sub->state;
-
- pj_gettimeofday(&now);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
- if (sub->role != PJSIP_ROLE_UAS)
- return -1;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY",
- sub, state[new_state].ptr));
-
- /* Lock subscription. */
- pj_mutex_lock(sub->mutex);
-
- /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */
- if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) {
- pj_assert(0);
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Update state no matter what. */
- sub_set_state(sub, new_state);
-
- /* Create transmit data. */
- tdata = pjsip_endpt_create_request_from_hdr( sub->endpt,
- &NOTIFY,
- sub->to->uri,
- sub->from, sub->to,
- sub->contact, sub->call_id,
- sub->cseq++,
- NULL);
- if (!tdata) {
- pj_mutex_unlock(sub->mutex);
- return -1;
- }
-
- /* Add Event header. */
- pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event));
-
- /* Add Subscription-State header. */
- ss_hdr = pjsip_sub_state_hdr_create(tdata->pool);
- ss_hdr->sub_state = state[new_state];
- ss_hdr->expires_param = sub->expiry_time.sec - now.sec;
- if (ss_hdr->expires_param < 0)
- ss_hdr->expires_param = 0;
- if (reason)
- pj_strdup(tdata->pool, &ss_hdr->reason_param, reason);
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr);
-
- /* Add Allow-Events header. */
- pjsip_msg_add_hdr( tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
-
- /* Add authentication */
- pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info);
-
- /* Route set. */
- route = sub->route_set.next;
- while (route != &sub->route_set) {
- pj_list_insert_before( &tdata->msg->hdr,
- pjsip_hdr_shallow_clone(tdata->pool, route));
- route = route->next;
- }
-
- /* Attach body. */
- tdata->msg->body = body;
-
- /* That's it, send! */
- status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response);
- if (status == 0)
- sub->pending_tsx++;
-
- /* If terminated notify application. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, reason);
- sub->pending_tsx--;
- }
- }
-
- /* Unlock subscription. */
- pj_mutex_unlock(sub->mutex);
-
- if (status != 0) {
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY",
- sub, state[sub->state].ptr));
- }
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
- return status;
-}
-
-
-/* If this timer callback is called, it means subscriber hasn't refreshed its
- * subscription on-time. Set the state to terminated. This will also send
- * NOTIFY with Subscription-State set to terminated.
- */
-static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
- pj_str_t reason = { "timeout", 7 };
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
-
- if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) {
- /* Notify application, but prevent app from destroying the sub. */
- ++sub->pending_tsx;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- --sub->pending_tsx;
- }
- //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED,
- // &reason, NULL);
- pj_mutex_unlock(sub->mutex);
-
-}
-
-/* Schedule notifier expiration. */
-static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay)
-{
- pj_time_val delay = { 0, 0 };
- pj_parsed_time pt;
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += sec_delay;
-
- sub->timer.id = TIMER_ID_UAS_EXPIRY;
- sub->timer.user_data = sub;
- sub->timer.cb = &uas_expire_timer_cb;
- delay.sec = sec_delay;
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay);
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay));
-}
-
-/* This timer is called for UAC to refresh the subscription. */
-static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry)
-{
- pjsip_event_sub *sub = entry->user_data;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer",
- sub, state[sub->state].ptr));
-
- pj_mutex_lock(sub->mutex);
- sub->timer.id = 0;
- send_sub_refresh(sub);
- pj_mutex_unlock(sub->mutex);
-}
-
-
-/* This will update the UAC's refresh schedule. */
-static void update_next_refresh(pjsip_event_sub *sub, int interval)
-{
- pj_time_val delay = {0, 0};
- pj_parsed_time pt;
-
- if (interval < SECONDS_BEFORE_EXPIRY) {
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): expiration delay too short (%d sec)! updated.",
- sub, state[sub->state].ptr, interval));
- interval = SECONDS_BEFORE_EXPIRY;
- }
-
- if (sub->timer.id != 0)
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
-
- sub->timer.id = TIMER_ID_REFRESH;
- sub->timer.user_data = sub;
- sub->timer.cb = &refresh_timer_cb;
- pj_gettimeofday(&sub->expiry_time);
- delay.sec = interval - SECONDS_BEFORE_EXPIRY;
- sub->expiry_time.sec += delay.sec;
-
- pj_time_decode(&sub->expiry_time, &pt);
- PJ_LOG(4,(THIS_FILE,
- "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)",
- sub, state[sub->state].ptr,
- pt.hour, pt.min, pt.sec,
- delay.sec));
-
- pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay );
-}
-
-
-/* Find subscription in the hash table.
- * If found, lock the subscription before returning to caller.
- */
-static pjsip_event_sub *find_sub(pjsip_rx_data *rdata)
-{
- pj_str_t key;
- pjsip_role_e role;
- pjsip_event_sub *sub;
- pjsip_method *method = &rdata->msg->line.req.method;
- pj_str_t *tag;
-
- if (rdata->msg->type == PJSIP_REQUEST_MSG) {
- if (pjsip_method_cmp(method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAS;
- tag = &rdata->to_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAC;
- tag = &rdata->to_tag;
- }
- } else {
- if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) {
- role = PJSIP_ROLE_UAC;
- tag = &rdata->from_tag;
- } else {
- pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0);
- role = PJSIP_ROLE_UAS;
- tag = &rdata->from_tag;
- }
- }
- create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag);
-
- pj_mutex_lock(mgr.mutex);
- sub = pj_hash_get(mgr.ht, key.ptr, key.slen);
- if (sub)
- pj_mutex_lock(sub->mutex);
- pj_mutex_unlock(mgr.mutex);
-
- return sub;
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message
- * to refresh existing subscription.
- */
-static void on_received_sub_refresh( pjsip_event_sub *sub,
- pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_hdr *e;
- pjsip_expires_hdr *expires;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = sub->state;
- int old_state = sub->state;
- int new_interval = 0;
- pjsip_tx_data *tdata;
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh",
- sub, state[sub->state].ptr));
-
- /* Check that the event matches. */
- hname = pj_str("Event");
- e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!e) {
- status = 400;
- reason_phrase = pj_str("Missing Event header");
- goto send_response;
- }
- if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&e->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check server state. */
- if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- goto send_response;
- }
-
- /* Check expires header. */
- expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (!expires) {
- /*
- status = 400;
- reason_phrase = pj_str("Missing Expires header");
- goto send_response;
- */
- new_interval = sub->default_interval;
- } else {
- /* Check that interval is not too short.
- * Note that expires time may be zero (for unsubscription).
- */
- new_interval = expires->ivalue;
- if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) {
- status = PJSIP_SC_INTERVAL_TOO_BRIEF;
- goto send_response;
- }
- }
-
- /* Update interval. */
- sub->default_interval = new_interval;
- pj_gettimeofday(&sub->expiry_time);
- sub->expiry_time.sec += new_interval;
-
- /* Update timer only if this is not unsubscription. */
- if (new_interval > 0) {
- sub->default_interval = new_interval;
- sub_schedule_uas_expire( sub, new_interval );
-
- /* Call callback. */
- if (sub->cb.on_received_refresh) {
- sub->pending_tsx++;
- (*sub->cb.on_received_refresh)(sub, rdata);
- sub->pending_tsx--;
- }
- }
-
-send_response:
- tdata = pjsip_endpt_create_response( sub->endpt, rdata, status);
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- /* Add Expires header. */
- expires = pjsip_expires_hdr_create(tdata->pool);
- expires->ivalue = sub->default_interval;
- pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires);
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_msg_add_hdr(tdata->msg,
- pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events));
- }
- /* Send down to transaction. */
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Notify application if sub is terminated. */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- sub_set_state(sub, new_state);
- if (new_state!=old_state && sub->cb.on_sub_terminated) {
- pj_str_t reason = {"", 0};
- if (reason_phrase.slen) reason = reason_phrase;
- else reason = *pjsip_get_status_text(status);
-
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- pj_mutex_unlock(sub->mutex);
-
- /* Prefer to call log when we're not holding the mutex. */
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d",
- sub, state[sub->state].ptr,
- (tdata ? tdata->obj_name : "null"), status));
-
- /* Check if application has requested deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- }
-
-}
-
-
-/* This function is called when we receive SUBSCRIBE request message for
- * a new subscription.
- */
-static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata )
-{
- package *pkg;
- pj_pool_t *pool;
- pjsip_event_sub *sub = NULL;
- pj_str_t hname;
- int status = 200;
- pj_str_t reason = { NULL, 0 };
- pjsip_tx_data *tdata;
- pjsip_expires_hdr *expires;
- pjsip_accept_hdr *accept;
- pjsip_event_hdr *evhdr;
-
- /* Get the Event header. */
- hname = pj_str("Event");
- evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!evhdr) {
- status = 400;
- reason = pj_str("No Event header in request");
- goto send_response;
- }
-
- /* Find corresponding package.
- * We don't lock the manager's mutex since we assume the package list
- * won't change once the application is running!
- */
- pkg = mgr.pkg_list.next;
- while (pkg != &mgr.pkg_list) {
- if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0)
- break;
- pkg = pkg->next;
- }
-
- if (pkg == &mgr.pkg_list) {
- /* Event type is not supported by any packages! */
- status = 489;
- reason = pj_str("Bad Event");
- goto send_response;
- }
-
- /* First check that the Accept specification matches the
- * package's Accept types.
- */
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- pj_str_t *content_type = NULL;
-
- for (i=0; i<accept->count && !content_type; ++i) {
- int j;
- for (j=0; j<pkg->accept_cnt; ++j) {
- if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) {
- content_type = &pkg->accept[j];
- break;
- }
- }
- }
-
- if (!content_type) {
- status = PJSIP_SC_NOT_ACCEPTABLE_HERE;
- goto send_response;
- }
- }
-
- /* Check whether the package wants to accept the subscription. */
- pj_assert(pkg->cb.on_query_subscribe != NULL);
- (*pkg->cb.on_query_subscribe)(rdata, &status);
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200))
- goto send_response;
-
- /* Create new subscription record. */
- pool = pjsip_endpt_create_pool(tsx->endpt, "esub",
- SUB_POOL_SIZE, SUB_POOL_INC);
- if (!pool) {
- status = 500;
- goto send_response;
- }
- sub = pj_pool_calloc(pool, 1, sizeof(*sub));
- sub->pool = pool;
- sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE);
- if (!sub->mutex) {
- status = 500;
- goto send_response;
- }
-
- PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub));
-
- /* Start locking mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* Init UAS subscription */
- sub->endpt = tsx->endpt;
- sub->role = PJSIP_ROLE_UAS;
- sub->state = PJSIP_EVENT_SUB_STATE_PENDING;
- sub->state_str = state[sub->state];
- pj_list_init(&sub->auth_sess);
- pj_list_init(&sub->route_set);
- sub->from = pjsip_hdr_clone(pool, rdata->to);
- pjsip_fromto_set_from(sub->from);
- if (sub->from->tag.slen == 0) {
- pj_create_unique_string(pool, &sub->from->tag);
- rdata->to->tag = sub->from->tag;
- }
- sub->to = pjsip_hdr_clone(pool, rdata->from);
- pjsip_fromto_set_to(sub->to);
- sub->contact = pjsip_contact_hdr_create(pool);
- sub->contact->uri = sub->from->uri;
- sub->call_id = pjsip_cid_hdr_create(pool);
- pj_strdup(pool, &sub->call_id->id, &rdata->call_id);
- sub->cseq = pj_rand() % 0xFFFF;
-
- expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL);
- if (expires) {
- sub->default_interval = expires->ivalue;
- if (sub->default_interval > 0 &&
- sub->default_interval < SECONDS_BEFORE_EXPIRY)
- {
- status = 423; /* Interval too short. */
- goto send_response;
- }
- } else {
- sub->default_interval = 600;
- }
-
- /* Clone Event header. */
- sub->event = pjsip_hdr_clone(pool, evhdr);
-
- /* Register to hash table. */
- create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id,
- &sub->from->tag);
- pj_mutex_lock(mgr.mutex);
- pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub);
- pj_mutex_unlock(mgr.mutex);
-
- /* Set timer where subscription will expire only when expires<>0.
- * Subscriber may send new subscription with expires==0.
- */
- if (sub->default_interval != 0) {
- sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY);
- }
-
- /* Notify application. */
- if (pkg->cb.on_subscribe) {
- pjsip_event_sub_cb *cb = NULL;
- sub->pending_tsx++;
- (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval);
- sub->pending_tsx--;
- if (cb == NULL)
- pj_memset(&sub->cb, 0, sizeof(*cb));
- else
- pj_memcpy(&sub->cb, cb, sizeof(*cb));
- }
-
-
-send_response:
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d",
- sub, state[sub->state].ptr, status));
-
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status);
- if (tdata) {
- if (reason.slen) {
- /* Customize reason text. */
- tdata->msg->line.status.reason = reason;
- }
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- /* Add Expires header. */
- pjsip_expires_hdr *hdr;
-
- hdr = pjsip_expires_hdr_create(tdata->pool);
- hdr->ivalue = sub->default_interval;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr );
- }
- if (status == 423) {
- /* Add Min-Expires header. */
- pjsip_min_expires_hdr *hdr;
-
- hdr = pjsip_min_expires_hdr_create(tdata->pool);
- hdr->ivalue = SECONDS_BEFORE_EXPIRY;
- pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr);
- }
- if (status == 489 ||
- status==PJSIP_SC_NOT_ACCEPTABLE_HERE ||
- PJSIP_IS_STATUS_IN_CLASS(status,200))
- {
- /* Add Allow-Events header. */
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr(tdata->msg, hdr);
-
- /* Should add Accept header?. */
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* If received new subscription with expires=0, terminate. */
- if (sub && sub->default_interval == 0) {
- pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason = { "timeout", 7 };
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) {
- if (sub && sub->mutex) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pjsip_endpt_destroy_pool(tsx->endpt, sub->pool);
- }
- } else {
- pj_assert(status >= 200);
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This is the main callback when SUBSCRIBE request is received. */
-static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub = find_sub(rdata);
-
- if (sub)
- on_received_sub_refresh(sub, tsx, rdata);
- else
- on_new_subscription(tsx, rdata);
-}
-
-
-/* This callback is called when response to SUBSCRIBE is received. */
-static void on_subscribe_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_transaction *tsx = event->obj.tsx;
- int new_state, old_state = sub->state;
-
- pj_assert(tsx->status_code >= 200);
- if (tsx->status_code < 200)
- return;
-
- pj_assert(sub->role == PJSIP_ROLE_UAC);
-
- /* Lock mutex. */
- pj_mutex_lock(sub->mutex);
-
- /* If request failed with 401/407 error, silently retry the request. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_subscribe_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) {
- /* Update To tag. */
- if (sub->to->tag.slen == 0)
- pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag);
-
- new_state = sub->state;
-
- } else if (tsx->status_code == 481) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
-
- } else if (tsx->status_code >= 300) {
- /* RFC 3265 Section 3.1.4.2:
- * If a SUBSCRIBE request to refresh a subscription fails
- * with a non-481 response, the original subscription is still
- * considered valid for the duration of original exires.
- *
- * Note:
- * Since we normally send SUBSCRIBE for refreshing the subscription,
- * it means the subscription already expired anyway. So we terminate
- * the subscription now.
- */
- if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- /* Use this to be compliant with Section 3.1.4.2
- new_state = sub->state;
- */
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- }
- } else {
- pj_assert(0);
- new_state = sub->state;
- }
-
- if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) {
- sub_set_state(sub, new_state);
- }
-
- if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state == PJSIP_EVENT_SUB_STATE_PENDING)
- {
- /*
- * Register timer for next subscription refresh, but only when
- * we're not unsubscribing. Also update default_interval and Expires
- * header.
- */
- if (sub->default_interval > 0 && !sub->delete_flag) {
- pjsip_expires_hdr *exp = NULL;
-
- /* Could be transaction timeout. */
- if (event->src_type == PJSIP_EVENT_RX_MSG) {
- exp = pjsip_msg_find_hdr(event->src.rdata->msg,
- PJSIP_H_EXPIRES, NULL);
- }
-
- if (exp) {
- int delay = exp->ivalue;
- if (delay > 0) {
- pj_time_val new_expiry;
- pj_gettimeofday(&new_expiry);
- new_expiry.sec += delay;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- //if (delay > 0 && delay < sub->default_interval) {
- sub->default_interval = delay;
- sub->uac_expires->ivalue = delay;
- update_next_refresh(sub, delay);
- }
- }
- }
- }
- }
-
- /* Call callback. */
- if (!sub->delete_flag) {
- if (sub->cb.on_received_sub_response) {
- (*sub->cb.on_received_sub_response)(sub, event);
- }
- }
-
- /* Notify application if we're terminated. */
- if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- pj_str_t reason;
- if (event->src_type == PJSIP_EVENT_RX_MSG)
- reason = event->src.rdata->msg->line.status.reason;
- else
- reason = *pjsip_get_status_text(tsx->status_code);
-
- (*sub->cb.on_sub_terminated)(sub, &reason);
- }
- }
-
- /* Decrement pending tsx count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */
-}
-
-/*
- * This callback called when we receive incoming NOTIFY request.
- */
-static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata)
-{
- pjsip_event_sub *sub;
- pjsip_tx_data *tdata;
- int status = 200;
- int old_state;
- pj_str_t reason = { NULL, 0 };
- pj_str_t reason_phrase = { NULL, 0 };
- int new_state = PJSIP_EVENT_SUB_STATE_NULL;
-
- /* Find subscription based on Call-ID and From tag.
- * This will also automatically lock the subscription, if it's found.
- */
- sub = find_sub(rdata);
- if (!sub) {
- /* RFC 3265: Section 3.2 Description of NOTIFY Behavior:
- * Answer with 481 Subscription does not exist.
- */
- PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!"));
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
-
- } else {
- pj_assert(sub->role == PJSIP_ROLE_UAC);
- PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY",
- sub, state[sub->state].ptr));
-
- }
-
- new_state = old_state = sub->state;
-
- /* RFC 3265: Section 3.2.1
- * Check that the Event header match the subscription.
- */
- if (status == 200) {
- pjsip_event_hdr *hdr;
- pj_str_t hname = { "Event", 5 };
-
- hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Event header found");
- } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 ||
- pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0)
- {
- status = 481;
- reason_phrase = pj_str("Subscription does not exist");
- }
- }
-
- /* Update subscription state and timer. */
- if (status == 200) {
- pjsip_sub_state_hdr *hdr;
- const pj_str_t hname = { "Subscription-State", 18 };
- const pj_str_t state_active = { "active", 6 },
- state_pending = { "pending", 7},
- state_terminated = { "terminated", 10 };
-
- hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL);
- if (!hdr) {
- status = PJSIP_SC_BAD_REQUEST;
- reason_phrase = pj_str("No Subscription-State header found");
- goto process;
- }
-
- /*
- * Update subscription state.
- */
- if (pj_stricmp(&hdr->sub_state, &state_active) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_ACTIVE;
- } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) {
- if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- new_state = PJSIP_EVENT_SUB_STATE_PENDING;
- } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) {
- new_state = PJSIP_EVENT_SUB_STATE_TERMINATED;
- } else {
- new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN;
- }
-
- reason = hdr->reason_param;
-
- if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL &&
- sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED)
- {
- sub_set_state(sub, new_state);
- if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) {
- pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state);
- } else {
- sub->state_str = state[new_state];
- }
- }
-
- /*
- * Update timeout timer in required, just in case notifier changed the
- * expiration to shorter time.
- * Section 3.2.2: the expires param can only shorten the interval.
- */
- if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE ||
- sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0)
- {
- pj_time_val now, new_expiry;
-
- pj_gettimeofday(&now);
- new_expiry.sec = now.sec + hdr->expires_param;
- if (sub->timer.id==0 ||
- new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2)
- {
- update_next_refresh(sub, hdr->expires_param);
- }
- }
- }
-
-process:
- /* Note: here we sub MAY BE NULL! */
-
- /* Send response to NOTIFY */
- tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status );
- if (tdata) {
- if (reason_phrase.slen)
- tdata->msg->line.status.reason = reason_phrase;
-
- if (PJSIP_IS_STATUS_IN_CLASS(status,200)) {
- pjsip_hdr *hdr;
- hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events);
- pjsip_msg_add_hdr( tdata->msg, hdr);
- }
-
- pjsip_tsx_on_tx_msg(tsx, tdata);
- }
-
- /* Call NOTIFY callback, if any. */
- if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) {
- sub->pending_tsx++;
- (*sub->cb.on_received_notify)(sub, rdata);
- sub->pending_tsx--;
- }
-
- /* Check if subscription is terminated and call callback. */
- if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) {
- if (sub->cb.on_sub_terminated) {
- sub->pending_tsx++;
- (*sub->cb.on_sub_terminated)(sub, &reason);
- sub->pending_tsx--;
- }
- }
-
- /* Check if application has requested deletion. */
- if (sub && sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else if (sub) {
- pj_mutex_unlock(sub->mutex);
- }
-}
-
-/* This callback is called when we received NOTIFY response. */
-static void on_notify_response(void *token, pjsip_event *event)
-{
- pjsip_event_sub *sub = token;
- pjsip_event_sub_state old_state = sub->state;
- pjsip_transaction *tsx = event->obj.tsx;
-
- /* Lock the subscription. */
- pj_mutex_lock(sub->mutex);
-
- pj_assert(sub->role == PJSIP_ROLE_UAS);
-
- /* If request failed with authorization failure, silently retry. */
- if (tsx->status_code==401 || tsx->status_code==407) {
- pjsip_tx_data *tdata;
- tdata = pjsip_auth_reinit_req(sub->endpt,
- sub->pool, &sub->auth_sess,
- sub->cred_cnt, sub->cred_info,
- tsx->last_tx, event->src.rdata );
- if (tdata) {
- int status;
- pjsip_cseq_hdr *cseq;
- cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL);
- cseq->cseq = sub->cseq++;
- status = pjsip_endpt_send_request( sub->endpt, tdata,
- -1, sub,
- &on_notify_response);
- if (status == 0) {
- pj_mutex_unlock(sub->mutex);
- return;
- }
- }
- }
-
- /* Notify application. */
- if (sub->cb.on_received_notify_response)
- (*sub->cb.on_received_notify_response)(sub, event);
-
- /* Check for response 481. */
- if (event->obj.tsx->status_code == 481) {
- /* Remote says that the subscription does not exist!
- * Terminate subscription!
- */
- sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED);
- if (sub->timer.id) {
- pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
- sub->timer.id = 0;
- }
-
- PJ_LOG(4, (THIS_FILE,
- "event_sub%p (%s): got 481 response to NOTIFY. Terminating...",
- sub, state[sub->state].ptr));
-
- /* Notify app. */
- if (sub->state!=old_state && sub->cb.on_sub_terminated)
- (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason);
- }
-
- /* Decrement pending transaction count. */
- --sub->pending_tsx;
- pj_assert(sub->pending_tsx >= 0);
-
- /* Check that the subscription is marked for deletion. */
- if (sub->delete_flag && sub->pending_tsx <= 0) {
- pjsip_event_sub_destroy(sub);
- } else {
- pj_mutex_unlock(sub->mutex);
- }
-
- /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */
-}
-
-
-/* This is the transaction handler for incoming SUBSCRIBE and NOTIFY
- * requests.
- */
-static void tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_msg *msg;
- pjsip_rx_data *rdata;
-
- /* Only want incoming message events. */
- if (event->src_type != PJSIP_EVENT_RX_MSG)
- return;
-
- rdata = event->src.rdata;
- msg = rdata->msg;
-
- /* Only want to process request messages. */
- if (msg->type != PJSIP_REQUEST_MSG)
- return;
-
- /* Only want the first notification. */
- if (event->obj.tsx && event->obj.tsx->status_code >= 100)
- return;
-
- if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) {
- /* Process incoming SUBSCRIBE request. */
- on_subscribe_request( event->obj.tsx, rdata );
- } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) {
- /* Process incoming NOTIFY request. */
- on_notify_request( event->obj.tsx, rdata );
- }
-}
-
diff --git a/pjsip/src/pjsip-simple/evsub.c b/pjsip/src/pjsip-simple/evsub.c
new file mode 100644
index 00000000..30bd8525
--- /dev/null
+++ b/pjsip/src/pjsip-simple/evsub.c
@@ -0,0 +1,1785 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * 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-simple/evsub.h>
+#include <pjsip-simple/evsub_msg.h>
+#include <pjsip-simple/errno.h>
+#include <pjsip/sip_errno.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_dialog.h>
+#include <pjsip/sip_auth.h>
+#include <pjsip/sip_transaction.h>
+#include <pjsip/sip_event.h>
+#include <pj/assert.h>
+#include <pj/guid.h>
+#include <pj/log.h>
+#include <pj/os.h>
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "evsub.c"
+
+/*
+ * Global constant
+ */
+
+/* Let's define this enum, so that it'll trigger compilation error
+ * when somebody define the same enum in sip_msg.h
+ */
+enum
+{
+ PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD,
+ PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD
+};
+
+const pjsip_method pjsip_subscribe_method =
+{
+ PJSIP_SUBSCRIBE_METHOD,
+ { "SUBSCRIBE", 9 }
+};
+
+const pjsip_method pjsip_notify_method =
+{
+ PJSIP_NOTIFY_METHOD,
+ { "NOTIFY", 6 }
+};
+
+/*
+ * Static prototypes.
+ */
+static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*);
+static pj_status_t mod_evsub_unload(void);
+
+
+/*
+ * State names.
+ */
+static pj_str_t evsub_state_names[] =
+{
+ { "NULL", 4},
+ { "SENT", 4},
+ { "ACCEPTED", 8},
+ { "PENDING", 7},
+ { "ACTIVE", 6},
+ { "TERMINATED", 10},
+ { "UNKNOWN", 7}
+};
+
+/*
+ * Timer constants.
+ */
+
+/* Number of seconds to send SUBSCRIBE before the actual expiration */
+#define TIME_UAC_REFRESH 5
+
+/* Time to wait for the final NOTIFY after sending unsubscription */
+#define TIME_UAC_TERMINATE 5
+
+/* If client responds NOTIFY with non-2xx final response (such as 401),
+ * wait for this seconds for further NOTIFY, otherwise client will
+ * unsubscribe
+ */
+#define TIME_UAC_WAIT_NOTIFY 5
+
+
+/*
+ * Timer id
+ */
+enum timer_id
+{
+ /* No timer. */
+ TIMER_TYPE_NONE,
+
+ /* Time to refresh client subscription.
+ * The action is to call on_client_refresh() callback.
+ */
+ TIMER_TYPE_UAC_REFRESH,
+
+ /* UAS timeout after to subscription refresh.
+ * The action is to call on_server_timeout() callback.
+ */
+ TIMER_TYPE_UAS_TIMEOUT,
+
+ /* UAC waiting for final NOTIFY after unsubscribing
+ * The action is to terminate.
+ */
+ TIMER_TYPE_UAC_TERMINATE,
+
+ /* UAC waiting for further NOTIFY after sending non-2xx response to
+ * NOTIFY. The action is to unsubscribe.
+ */
+ TIMER_TYPE_UAC_WAIT_NOTIFY,
+
+};
+
+static const char *timer_names[] =
+{
+ "None",
+ "UAC_REFRESH",
+ "UAS_TIMEOUT"
+ "UAC_TERMINATE",
+ "UAC_WAIT_NOTIFY",
+};
+
+/*
+ * Definition of event package.
+ */
+struct evpkg
+{
+ PJ_DECL_LIST_MEMBER(struct evpkg);
+
+ pj_str_t pkg_name;
+ pjsip_module *pkg_mod;
+ unsigned pkg_expires;
+ pjsip_accept_hdr *pkg_accept;
+};
+
+
+/*
+ * Event subscription module (mod-evsub).
+ */
+static struct mod_evsub
+{
+ pjsip_module mod;
+ pj_pool_t *pool;
+ pjsip_endpoint *endpt;
+ struct evpkg pkg_list;
+ pjsip_allow_events_hdr *allow_events_hdr;
+
+} mod_evsub =
+{
+ {
+ NULL, NULL, /* prev, next. */
+ { "mod-evsub", 9 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */
+ NULL, /* User data. */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ &mod_evsub_unload, /* unload() */
+ NULL, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ &mod_evsub_on_tsx_state, /* on_tsx_state() */
+ }
+};
+
+
+/*
+ * Event subscription session.
+ */
+struct pjsip_evsub
+{
+ char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */
+ pj_pool_t *pool; /**< Pool. */
+ pjsip_endpoint *endpt; /**< Endpoint instance. */
+ pjsip_dialog *dlg; /**< Underlying dialog. */
+ struct evpkg *pkg; /**< The event package. */
+ pjsip_evsub_user user; /**< Callback. */
+ pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */
+ pjsip_evsub_state state; /**< Subscription state. */
+ pj_str_t state_str; /**< String describing the state. */
+ pjsip_evsub_state dst_state; /**< Pending state to be set. */
+ pj_str_t dst_state_str;/**< Pending state to be set. */
+ pjsip_method method; /**< Method that established subscr.*/
+ pjsip_event_hdr *event; /**< Event description. */
+ pjsip_expires_hdr *expires; /**< Expires header */
+ pjsip_accept_hdr *accept; /**< Local Accept header. */
+
+ pj_time_val refresh_time; /**< Time to refresh. */
+ pj_timer_entry timer; /**< Internal timer. */
+ int pending_tsx; /**< Number of pending transactions.*/
+
+ void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */
+};
+
+
+/*
+ * This is the structure that will be "attached" to dialog.
+ * The purpose is to allow multiple subscriptions inside a dialog.
+ */
+struct dlgsub
+{
+ PJ_DECL_LIST_MEMBER(struct dlgsub);
+ pjsip_evsub *sub;
+};
+
+
+/* Static vars. */
+static const pj_str_t STR_EVENT = { "Event", 5 };
+static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 };
+static const pj_str_t STR_TERMINATED = { "terminated", 10 };
+static const pj_str_t STR_ACTIVE = { "active", 6 };
+static const pj_str_t STR_PENDING = { "pending", 7 };
+static const pj_str_t STR_TIMEOUT = { "timeout", 7};
+
+/*
+ * On unload module.
+ */
+static pj_status_t mod_evsub_unload(void)
+{
+ pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool);
+ mod_evsub.pool = NULL;
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Init and register module.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt)
+{
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL);
+ PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP);
+
+ /* Keep endpoint for future reference: */
+ mod_evsub.endpt = endpt;
+
+ /* Init event package list: */
+ pj_list_init(&mod_evsub.pkg_list);
+
+ /* Create pool: */
+ mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000);
+ if (!mod_evsub.pool)
+ return PJ_ENOMEM;
+
+ /* Register module: */
+ status = pjsip_endpt_register_module(endpt, &mod_evsub.mod);
+ if (status != PJ_SUCCESS)
+ goto on_error;
+
+ /* Create Allow-Events header: */
+ mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool);
+
+ /* Register SIP-event specific headers parser: */
+ pjsip_evsub_init_parser();
+
+ return PJ_SUCCESS;
+
+on_error:
+ if (mod_evsub.pool) {
+ pjsip_endpt_release_pool(endpt, mod_evsub.pool);
+ mod_evsub.pool = NULL;
+ }
+ mod_evsub.endpt = NULL;
+ return status;
+}
+
+
+/*
+ * Get the instance of the module.
+ */
+PJ_DEF(pjsip_module*) pjsip_evsub_instance(void)
+{
+ PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL);
+
+ return &mod_evsub.mod;
+}
+
+
+/*
+ * Get the event subscription instance in the transaction.
+ */
+PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx)
+{
+ return tsx->mod_data[mod_evsub.mod.id];
+}
+
+
+/*
+ * Set event subscription's module data.
+ */
+PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id,
+ void *data )
+{
+ PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return);
+ sub->mod_data[mod_id] = data;
+}
+
+
+/*
+ * Get event subscription's module data.
+ */
+PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id )
+{
+ PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL);
+ return sub->mod_data[mod_id];
+}
+
+
+/*
+ * Find registered event package with matching name.
+ */
+static struct evpkg* find_pkg(const pj_str_t *event_name)
+{
+ struct evpkg *pkg;
+
+ pkg = mod_evsub.pkg_list.next;
+ while (pkg != &mod_evsub.pkg_list) {
+
+ if (pj_stricmp(&pkg->pkg_name, event_name) == 0) {
+ return pkg;
+ }
+
+ pkg = pkg->next;
+ }
+
+ return NULL;
+}
+
+/*
+ * Register an event package
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod,
+ const pj_str_t *event_name,
+ unsigned expires,
+ unsigned accept_cnt,
+ const pj_str_t accept[])
+{
+ struct evpkg *pkg;
+ unsigned i;
+
+ PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL);
+ PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values),
+ PJ_ETOOMANY);
+
+ /* Make sure no module with the specified name already registered: */
+
+ PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS);
+
+
+ /* Create new event package: */
+
+ pkg = pj_pool_alloc(mod_evsub.pool, sizeof(struct evpkg));
+ pkg->pkg_mod = pkg_mod;
+ pkg->pkg_expires = expires;
+ pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name);
+
+ pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool);
+ pkg->pkg_accept->count = accept_cnt;
+ for (i=0; i<accept_cnt; ++i) {
+ pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]);
+ }
+
+ /* Add to package list: */
+
+ pj_list_push_back(&mod_evsub.pkg_list, pkg);
+
+ /* Add to Allow-Events header: */
+
+ if (mod_evsub.allow_events_hdr->count !=
+ PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values))
+ {
+ mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] =
+ pkg->pkg_name;
+ ++mod_evsub.allow_events_hdr->count;
+ }
+
+
+ /* Done */
+
+ PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s",
+ (int)event_name->slen, event_name->ptr,
+ (int)pkg_mod->name.slen, pkg_mod->name.ptr));
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Update expiration time.
+ */
+static void update_expires( pjsip_evsub *sub, pj_uint32_t interval )
+{
+ pj_gettimeofday(&sub->refresh_time);
+ sub->refresh_time.sec += interval;
+}
+
+
+/*
+ * Schedule timer.
+ */
+static void set_timer( pjsip_evsub *sub, int timer_id,
+ pj_int32_t seconds)
+{
+ if (sub->timer.id != TIMER_TYPE_NONE) {
+ PJ_LOG(5,(sub->obj_name, "%s %s timer",
+ (timer_id==sub->timer.id ? "Updating" : "Cancelling"),
+ timer_names[sub->timer.id]));
+ pjsip_endpt_cancel_timer(sub->endpt, &sub->timer);
+ sub->timer.id = TIMER_TYPE_NONE;
+ }
+
+ if (timer_id != TIMER_TYPE_NONE) {
+ pj_time_val timeout;
+
+ PJ_ASSERT_ON_FAIL(seconds > 0, return);
+
+ timeout.sec = seconds;
+ timeout.msec = 0;
+ sub->timer.id = timer_id;
+
+ pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout);
+
+ PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds",
+ timer_names[sub->timer.id], timeout.sec));
+ }
+}
+
+
+/*
+ * Destroy session.
+ */
+static void evsub_destroy( pjsip_evsub *sub )
+{
+ struct dlgsub *dlgsub_head, *dlgsub;
+
+ PJ_LOG(4,(sub->obj_name, "Subscription destroyed"));
+
+ /* Kill timer */
+ set_timer(sub, TIMER_TYPE_NONE, 0);
+
+ /* Remote this session from dialog's list of subscription */
+ dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id];
+ dlgsub = dlgsub_head->next;
+ while (dlgsub != dlgsub_head) {
+
+ if (dlgsub->sub == sub) {
+ pj_list_erase(dlgsub);
+ break;
+ }
+
+ dlgsub = dlgsub->next;
+ }
+
+ /* Decrement dialog's session */
+ pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod);
+}
+
+/*
+ * Set subscription session state.
+ */
+static void set_state( pjsip_evsub *sub, pjsip_evsub_state state,
+ const pj_str_t *state_str, pjsip_event *event)
+{
+ pj_str_t old_state_str = sub->state_str;
+
+ sub->state = state;
+
+ if (state_str && state_str->slen)
+ pj_strdup_with_null(sub->pool, &sub->state_str, state_str);
+ else
+ sub->state_str = evsub_state_names[state];
+
+ PJ_LOG(4,(sub->obj_name,
+ "Subscription state changed %.*s --> %.*s",
+ (int)old_state_str.slen,
+ old_state_str.ptr,
+ (int)sub->state_str.slen,
+ sub->state_str.ptr));
+
+ if (sub->user.on_evsub_state)
+ (*sub->user.on_evsub_state)(sub, event);
+
+ if (state == PJSIP_EVSUB_STATE_TERMINATED) {
+
+ if (sub->pending_tsx == 0) {
+ evsub_destroy(sub);
+ }
+ }
+}
+
+
+/*
+ * Timer callback.
+ */
+static void on_timer( pj_timer_heap_t *timer_heap,
+ struct pj_timer_entry *entry)
+{
+ pjsip_evsub *sub;
+ int timer_id;
+
+ PJ_UNUSED_ARG(timer_heap);
+
+ sub = entry->user_data;
+
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ timer_id = entry->id;
+ entry->id = TIMER_TYPE_NONE;
+
+ switch (timer_id) {
+
+ case TIMER_TYPE_UAC_REFRESH:
+ /* Time for UAC to refresh subscription */
+ if (sub->user.on_client_refresh) {
+ (*sub->user.on_client_refresh)(sub);
+ } else {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_LOG(5,(sub->obj_name, "Refreshing subscription."));
+ status = pjsip_evsub_initiate(sub, &sub->method,
+ sub->expires->ivalue,
+ &tdata);
+ if (status == PJ_SUCCESS)
+ pjsip_evsub_send_request(sub, tdata);
+ }
+ break;
+
+ case TIMER_TYPE_UAS_TIMEOUT:
+ /* Refresh from UAC has not been received */
+ if (sub->user.on_server_timeout) {
+ (*sub->user.on_server_timeout)(sub);
+ } else {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. "
+ "Sending NOTIFY to terminate."));
+ status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
+ NULL, &STR_TIMEOUT, &tdata);
+ if (status == PJ_SUCCESS)
+ pjsip_evsub_send_request(sub, tdata);
+ }
+ break;
+
+ case TIMER_TYPE_UAC_TERMINATE:
+ {
+ pjsip_event event;
+ pj_str_t reason = { "unsubscribing", 13};
+
+ PJSIP_EVENT_INIT_TIMER(event, entry);
+ PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. "
+ "Terminating.."));
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &event);
+ }
+ break;
+
+ case TIMER_TYPE_UAC_WAIT_NOTIFY:
+ {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_LOG(5,(sub->obj_name,
+ "Timeout waiting for subsequent NOTIFY (we did "
+ "send non-2xx response for previous NOTIFY). "
+ "Unsubscribing.."));
+ status = pjsip_evsub_initiate( sub, &sub->method, 0, &tdata);
+ if (status == PJ_SUCCESS)
+ pjsip_evsub_send_request(sub, tdata);
+ }
+ break;
+
+ default:
+ pj_assert(!"Invalid timer id");
+ }
+
+ pjsip_dlg_dec_lock(sub->dlg);
+}
+
+
+/*
+ * Create subscription session, used for both client and notifier.
+ */
+static pj_status_t evsub_create( pjsip_dialog *dlg,
+ pjsip_role_e role,
+ const pjsip_evsub_user *user_cb,
+ const pj_str_t *event,
+ pjsip_evsub **p_evsub )
+{
+ pjsip_evsub *sub;
+ struct evpkg *pkg;
+ struct dlgsub *dlgsub_head, *dlgsub;
+ pj_status_t status;
+
+ /* Make sure there's package register for the event name: */
+
+ pkg = find_pkg(event);
+ if (pkg == NULL)
+ return PJSIP_SIMPLE_ENOPKG;
+
+
+ /* Init attributes: */
+
+ sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub));
+ sub->pool = dlg->pool;
+ sub->endpt = dlg->endpt;
+ sub->dlg = dlg;
+ sub->pkg = pkg;
+ sub->role = role;
+ sub->state = PJSIP_EVSUB_STATE_NULL;
+ sub->state_str = evsub_state_names[sub->state];
+ sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires);
+ sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept);
+
+ sub->timer.user_data = sub;
+ sub->timer.cb = &on_timer;
+
+ /* Set name. */
+ pj_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name),
+ "evsub%p", sub);
+
+
+ /* Copy callback, if any: */
+ if (user_cb)
+ pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user));
+
+
+ /* Create Event header: */
+ sub->event = pjsip_event_hdr_create(sub->pool);
+ pj_strdup(sub->pool, &sub->event->event_type, event);
+
+
+ /* Create subcription list: */
+
+ dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
+ dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub));
+ dlgsub->sub = sub;
+
+ pj_list_init(dlgsub_head);
+ pj_list_push_back(dlgsub_head, dlgsub);
+
+
+ /* Register as dialog usage: */
+
+ status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head);
+ if (status != PJ_SUCCESS)
+ return status;
+
+
+ PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s",
+ (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"),
+ dlg->obj_name));
+
+ *p_evsub = sub;
+
+ return PJ_SUCCESS;
+}
+
+
+
+/*
+ * Create client subscription session.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ const pj_str_t *event,
+ pjsip_evsub **p_evsub)
+{
+ pjsip_evsub *sub;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL);
+
+ pjsip_dlg_inc_lock(dlg);
+ status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, &sub);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Add unique Id to Event header */
+ pj_create_unique_string(sub->pool, &sub->event->id_param);
+
+ /* Increment dlg session. */
+ pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod);
+
+ /* Done */
+ *p_evsub = sub;
+
+on_return:
+ pjsip_dlg_dec_lock(dlg);
+ return status;
+}
+
+
+/*
+ * Create server subscription session from incoming request.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_rx_data *rdata,
+ pjsip_evsub **p_evsub)
+{
+ pjsip_evsub *sub;
+ pjsip_transaction *tsx;
+ pjsip_accept_hdr *accept_hdr;
+ pjsip_event_hdr *event_hdr;
+ pjsip_expires_hdr *expires_hdr;
+ pj_status_t status;
+
+ /* Check arguments: */
+ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
+
+ /* MUST be request message: */
+ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+
+ /* Transaction MUST have been created (in the dialog) */
+ tsx = pjsip_rdata_get_tsx(rdata);
+ PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX);
+
+ /* No subscription must have been attached to transaction */
+ PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL,
+ PJSIP_ETYPEEXISTS);
+
+ /* Package MUST implement on_rx_refresh */
+ PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP);
+
+ /* Request MUST have "Event" header: */
+
+ event_hdr = (pjsip_event_hdr*)
+ pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
+ if (event_hdr == NULL) {
+ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
+ }
+
+ /* Start locking the mutex: */
+
+ pjsip_dlg_inc_lock(dlg);
+
+ /* Create the session: */
+
+ status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb,
+ &event_hdr->event_type, &sub);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Just duplicate Event header from the request */
+ sub->event = pjsip_hdr_clone(sub->pool, event_hdr);
+
+ /* Set the method: */
+ pjsip_method_copy(sub->pool, &sub->method,
+ &rdata->msg_info.msg->line.req.method);
+
+ /* Update expiration time according to client request: */
+
+ expires_hdr = (pjsip_expires_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+ if (expires_hdr) {
+ sub->expires->ivalue = expires_hdr->ivalue;
+ }
+
+ /* Update time. */
+ update_expires(sub, sub->expires->ivalue);
+
+ /* Update Accept header: */
+
+ accept_hdr = (pjsip_accept_hdr*)
+ pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
+ if (accept_hdr)
+ sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr);
+
+ /* We can start the session: */
+
+ pjsip_dlg_inc_session(dlg, &mod_evsub.mod);
+ sub->pending_tsx++;
+ tsx->mod_data[mod_evsub.mod.id] = sub;
+
+
+ /* Done. */
+ *p_evsub = sub;
+
+
+on_return:
+ pjsip_dlg_dec_lock(dlg);
+ return status;
+}
+
+
+/*
+ * Get subscription state.
+ */
+PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub)
+{
+ return sub->state;
+}
+
+/*
+ * Get state name.
+ */
+PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub)
+{
+ return sub->state_str.ptr;
+}
+
+
+/*
+ * Initiate client subscription
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub,
+ const pjsip_method *method,
+ pj_int32_t expires,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
+
+ /* Use SUBSCRIBE if method is not specified */
+ if (method == NULL)
+ method = &pjsip_subscribe_method;
+
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ /* Update method: */
+ if (sub->state == PJSIP_EVSUB_STATE_NULL)
+ pjsip_method_copy(sub->pool, &sub->method, method);
+
+ status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Add Event header: */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+
+ /* Update and add expires header: */
+ if (expires >= 0)
+ sub->expires->ivalue = expires;
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
+
+ /* Add Accept header: */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->accept));
+
+
+ /* Add Allow-Events header: */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool,
+ mod_evsub.allow_events_hdr));
+
+
+ *p_tdata = tdata;
+
+
+on_return:
+
+ pjsip_dlg_dec_lock(sub->dlg);
+ return status;
+}
+
+
+/*
+ * Accept incoming subscription request.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pjsip_hdr *hdr_list )
+{
+ pjsip_tx_data *tdata;
+ pjsip_transaction *tsx;
+ pj_status_t status;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL);
+
+ /* Can only be for server subscription: */
+ PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP);
+
+ /* Only expect 2xx status code (for now) */
+ PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP);
+
+ /* Subscription MUST have been attached to the transaction.
+ * Initial subscription request will be attached on evsub_create_uas(),
+ * while subsequent requests will be attached in tsx_state()
+ */
+ tsx = pjsip_rdata_get_tsx(rdata);
+ PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL,
+ PJ_EINVALIDOP);
+
+ /* Lock dialog */
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ /* Create response: */
+ status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Add expires header: */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->expires));
+
+
+ /* Send the response: */
+ status = pjsip_dlg_send_response( sub->dlg, tsx, tdata );
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+on_return:
+
+ pjsip_dlg_dec_lock(sub->dlg);
+ return status;
+}
+
+
+/*
+ * Create Subscription-State header based on current server subscription
+ * state.
+ */
+static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool,
+ pjsip_evsub *sub,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason )
+{
+ pjsip_sub_state_hdr *sub_state;
+ pj_time_val now, delay;
+
+ /* Get the remaining time before refresh is required */
+ pj_gettimeofday(&now);
+ delay = sub->refresh_time;
+ PJ_TIME_VAL_SUB(delay, now);
+
+ /* Create the Subscription-State header */
+ sub_state = pjsip_sub_state_hdr_create(pool);
+
+ /* Fill up the header */
+ switch (state) {
+ case PJSIP_EVSUB_STATE_SENT:
+ case PJSIP_EVSUB_STATE_ACCEPTED:
+ pj_assert(!"Invalid state!");
+ /* Treat as pending */
+
+ case PJSIP_EVSUB_STATE_PENDING:
+ sub_state->sub_state = STR_PENDING;
+ sub_state->expires_param = delay.sec;
+ break;
+
+ case PJSIP_EVSUB_STATE_ACTIVE:
+ sub_state->sub_state = STR_ACTIVE;
+ sub_state->expires_param = delay.sec;
+ break;
+
+ case PJSIP_EVSUB_STATE_TERMINATED:
+ sub_state->sub_state = STR_TERMINATED;
+ if (reason != NULL)
+ pj_strdup(pool, &sub_state->reason_param, reason);
+ break;
+
+ case PJSIP_EVSUB_STATE_UNKNOWN:
+ pj_assert(state_str != NULL);
+ pj_strdup(pool, &sub_state->sub_state, state_str);
+ break;
+ }
+
+ return sub_state;
+}
+
+/*
+ * Create and send NOTIFY request.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_sub_state_hdr *sub_state;
+ pj_status_t status;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL);
+
+ /* Lock dialog. */
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ /* Create NOTIFY request */
+ status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1,
+ &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Add Event header */
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, sub->event));
+
+ /* Add Subscription-State header */
+ sub_state = sub_state_create(tdata->pool, sub, state, state_str,
+ reason);
+ pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state);
+
+ /* Add Allow-Events header */
+ pjsip_msg_add_hdr(tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr));
+
+ /* Add Authentication headers. */
+ pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata );
+
+
+
+ /* Save destination state. */
+ sub->dst_state = state;
+ if (state_str)
+ pj_strdup(sub->pool, &sub->dst_state_str, state_str);
+ else
+ sub->dst_state_str.slen = 0;
+
+
+ *p_tdata = tdata;
+
+on_return:
+ /* Unlock dialog */
+ pjsip_dlg_dec_lock(sub->dlg);
+ return status;
+}
+
+
+/*
+ * Create NOTIFY to reflect current status.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub,
+ pjsip_tx_data **p_tdata )
+{
+ return pjsip_evsub_notify( sub, sub->state, &sub->state_str,
+ NULL, p_tdata );
+}
+
+
+/*
+ * Send request.
+ */
+PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub,
+ pjsip_tx_data *tdata)
+{
+ pj_status_t status;
+
+ /* Must be request message. */
+ PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+
+ /* Lock */
+ pjsip_dlg_inc_lock(sub->dlg);
+
+ /* Send the request. */
+ status = pjsip_dlg_send_request(sub->dlg, tdata, NULL);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Special case for NOTIFY:
+ * The new state was set in pjsip_evsub_notify(), but we apply the
+ * new state now, when the request was actually sent.
+ */
+ if (pjsip_method_cmp(&tdata->msg->line.req.method,
+ &pjsip_notify_method)==0)
+ {
+ PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL,
+ {goto on_return;});
+
+ set_state(sub, sub->dst_state,
+ (sub->dst_state_str.slen ? &sub->dst_state_str : NULL),
+ NULL);
+
+ sub->dst_state = PJSIP_EVSUB_STATE_NULL;
+ sub->dst_state_str.slen = 0;
+
+ }
+
+
+on_return:
+ pjsip_dlg_dec_lock(sub->dlg);
+ return status;
+}
+
+
+
+/*
+ * Attach subscription session to newly created transaction, if appropriate.
+ */
+static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ /*
+ * Newly created transaction will not have subscription session
+ * attached to it. Find the subscription session from the dialog,
+ * by matching the Event header.
+ */
+ pjsip_dialog *dlg;
+ pjsip_event_hdr *event_hdr;
+ pjsip_msg *msg;
+ struct dlgsub *dlgsub_head, *dlgsub;
+ pjsip_evsub *sub;
+
+ dlg = pjsip_tsx_get_dlg(tsx);
+ if (!dlg) {
+ pj_assert(!"Transaction should have a dialog instance!");
+ return NULL;
+ }
+
+ switch (event->body.tsx_state.type) {
+ case PJSIP_EVENT_RX_MSG:
+ msg = event->body.tsx_state.src.rdata->msg_info.msg;
+ break;
+ case PJSIP_EVENT_TX_MSG:
+ msg = event->body.tsx_state.src.tdata->msg;
+ break;
+ default:
+ if (tsx->role == PJSIP_ROLE_UAC)
+ msg = tsx->last_tx->msg;
+ else
+ msg = NULL;
+ break;
+ }
+
+ if (!msg) {
+ pj_assert(!"First transaction event is not TX or RX!");
+ return NULL;
+ }
+
+ event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
+ if (!event_hdr) {
+ /* Not subscription related message */
+ return NULL;
+ }
+
+ /* Find the subscription in the dialog, based on the content
+ * of Event header:
+ */
+
+ dlgsub_head = dlg->mod_data[mod_evsub.mod.id];
+ if (dlgsub_head == NULL) {
+ dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub));
+ pj_list_init(dlgsub_head);
+ dlg->mod_data[mod_evsub.mod.id] = dlgsub_head;
+ }
+ dlgsub = dlgsub_head->next;
+
+ while (dlgsub != dlgsub_head) {
+
+ /* Match event type and Id */
+ if (pj_strcmp(&dlgsub->sub->event->id_param, &event_hdr->id_param)==0 &&
+ pj_stricmp(&dlgsub->sub->event->event_type, &event_hdr->event_type)==0)
+ {
+ break;
+ }
+ dlgsub = dlgsub->next;
+ }
+
+ if (dlgsub == dlgsub_head) {
+ /* This could be incoming request to create new subscription */
+ PJ_LOG(4,(THIS_FILE,
+ "Subscription not found for %.*s, event=%.*s;id=%.*s",
+ (int)event_hdr->event_type.slen,
+ event_hdr->event_type.ptr,
+ (int)event_hdr->id_param.slen,
+ event_hdr->id_param.ptr));
+
+ /* If this is an incoming NOTIFY, reject with 481 */
+ if (tsx->state == PJSIP_TSX_STATE_TRYING &&
+ pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0)
+ {
+ pj_str_t reason = pj_str("Subscription Does Not Exist");
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_dlg_create_response(dlg,
+ event->body.tsx_state.src.rdata,
+ 481, &reason,
+ &tdata);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_dlg_send_response(dlg, tsx, tdata);
+ }
+ }
+ return NULL;
+ }
+
+ /* Found! */
+ sub = dlgsub->sub;
+
+ /* Attach session to the transaction */
+ tsx->mod_data[mod_evsub.mod.id] = sub;
+ sub->pending_tsx++;
+
+ return sub;
+}
+
+
+/*
+ * Create response, adding custome headers and msg body.
+ */
+static pj_status_t create_response( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pj_str_t *st_text,
+ const pjsip_hdr *res_hdr,
+ const pjsip_msg_body *body,
+ pjsip_tx_data **p_tdata)
+{
+ pjsip_tx_data *tdata;
+ pjsip_hdr *hdr;
+ pj_status_t status;
+
+ status = pjsip_dlg_create_response(sub->dlg, rdata,
+ st_code, st_text, &tdata);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ *p_tdata = tdata;
+
+ /* Add response headers. */
+ hdr = res_hdr->next;
+ while (hdr != res_hdr) {
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_clone(tdata->pool, hdr));
+ hdr = hdr->next;
+ }
+
+ /* Add msg body, if any */
+ if (body) {
+ tdata->msg->body = pj_pool_zalloc(tdata->pool,
+ sizeof(pjsip_msg_body));
+ status = pjsip_msg_body_clone(tdata->pool,
+ tdata->msg->body,
+ body);
+ if (status != PJ_SUCCESS) {
+ tdata->msg->body = NULL;
+ /* Ignore */
+ return PJ_SUCCESS;
+ }
+ }
+
+ return PJ_SUCCESS;
+}
+
+/*
+ * Get subscription state from the value of Subscription-State header.
+ */
+static void get_hdr_state( pjsip_sub_state_hdr *sub_state,
+ pjsip_evsub_state *state,
+ pj_str_t **state_str )
+{
+ if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) {
+
+ *state = PJSIP_EVSUB_STATE_TERMINATED;
+ *state_str = NULL;
+
+ } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) {
+
+ *state = PJSIP_EVSUB_STATE_ACTIVE;
+ *state_str = NULL;
+
+ } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) {
+
+ *state = PJSIP_EVSUB_STATE_PENDING;
+ *state_str = NULL;
+
+ } else {
+
+ *state = PJSIP_EVSUB_STATE_UNKNOWN;
+ *state_str = &sub_state->sub_state;
+
+ }
+}
+
+/*
+ * Transaction event processing by UAC, after subscription is sent.
+ */
+static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx,
+ pjsip_event *event )
+{
+
+ if (pjsip_method_cmp(&tsx->method, &sub->method)==0) {
+
+ /* Received response to outgoing request that establishes/refresh
+ * subscription.
+ */
+
+ /* First time initial request is sent. */
+ if (sub->state == PJSIP_EVSUB_STATE_NULL &&
+ tsx->state == PJSIP_TSX_STATE_CALLING)
+ {
+ set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event);
+ return;
+ }
+
+ /* Only interested in final response */
+ if (tsx->state != PJSIP_TSX_STATE_COMPLETED &&
+ tsx->state != PJSIP_TSX_STATE_TERMINATED)
+ {
+ return;
+ }
+
+ /* Handle authentication. */
+ if (tsx->status_code==401 || tsx->status_code==407) {
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+ /* Previously failed transaction has terminated */
+ return;
+ }
+
+ status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess,
+ event->body.tsx_state.src.rdata,
+ tsx->last_tx, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_dlg_send_request(sub->dlg, tdata, NULL);
+
+ if (status != PJ_SUCCESS) {
+ /* Authentication failed! */
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
+ NULL,
+ event);
+ return;
+ }
+
+ return;
+ }
+
+ if (tsx->status_code/100 == 2) {
+
+ /* Successfull SUBSCRIBE request!
+ * This could be:
+ * - response to initial SUBSCRIBE request
+ * - response to subsequent refresh
+ * - response to unsubscription
+ */
+
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+ /* Ignore; this transaction has been processed before */
+ return;
+ }
+
+ /* Update UAC refresh time, if response contains Expires header,
+ * only when we're not unsubscribing.
+ */
+ if (sub->expires->ivalue != 0) {
+ pjsip_msg *msg;
+ pjsip_expires_hdr *expires;
+
+ msg = event->body.tsx_state.src.rdata->msg_info.msg;
+ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+ if (expires) {
+ sub->expires->ivalue = expires->ivalue;
+ }
+ }
+
+ /* Update time */
+ update_expires(sub, sub->expires->ivalue);
+
+ /* Start UAC refresh timer, only when we're not unsubscribing */
+ if (sub->expires->ivalue != 0) {
+ unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ?
+ sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue;
+
+ PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds",
+ timeout));
+ set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
+
+ } else {
+ /* Otherwise set timer to terminate client subscription when
+ * NOTIFY to end subscription is not received.
+ */
+ set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE);
+ }
+
+ /* Set state, if necessary */
+ pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL);
+ if (sub->state == PJSIP_EVSUB_STATE_SENT) {
+ set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event);
+ }
+
+ } else {
+
+ /* Failed SUBSCRIBE request!
+ *
+ * The RFC 3265 says that if outgoing SUBSCRIBE fails with status
+ * other than 481, the subscription is still considered valid for
+ * the duration of the last Expires.
+ *
+ * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before
+ * expiration, theoritically the expiration is still valid for the
+ * next 5 seconds even when we receive non-481 failed response.
+ *
+ * Ah, what the heck!
+ *
+ * Just terminate now!
+ *
+ */
+
+ if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) {
+ /* Ignore, has been handled before */
+ return;
+ }
+
+ /* Kill any timer. */
+ set_timer(sub, TIMER_TYPE_NONE, 0);
+
+ /* Set state to TERMINATED */
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED,
+ NULL, event);
+
+ }
+
+ } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) {
+
+ /* Incoming NOTIFY.
+ * This can be the result of:
+ * - Initial subscription response
+ * - UAS updating the resource info.
+ * - Unsubscription response.
+ */
+ int st_code = 200;
+ pj_str_t *st_text = NULL;
+ pjsip_hdr res_hdr;
+ pjsip_msg_body *body = NULL;
+
+ pjsip_rx_data *rdata;
+ pjsip_msg *msg;
+ pjsip_sub_state_hdr *sub_state;
+
+ pjsip_evsub_state new_state;
+ pj_str_t *new_state_str;
+
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+ int next_refresh;
+
+ /* Only want to handle initial NOTIFY receive event. */
+ if (tsx->state != PJSIP_TSX_STATE_TRYING)
+ return;
+
+
+ rdata = event->body.tsx_state.src.rdata;
+ msg = rdata->msg_info.msg;
+
+ pj_list_init(&res_hdr);
+
+ /* Get subscription state header. */
+ sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL);
+ if (sub_state == NULL) {
+
+ pjsip_warning_hdr *warn_hdr;
+ pj_str_t warn_text = { "Missing Subscription-State header", 33};
+
+ /* Bad request! Add warning header. */
+ st_code = PJSIP_SC_BAD_REQUEST;
+ warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
+ pjsip_endpt_name(sub->endpt),
+ &warn_text);
+ pj_list_push_back(&res_hdr, warn_hdr);
+ }
+
+ /* Call application registered callback to handle incoming NOTIFY,
+ * if any.
+ */
+ if (st_code==200 && sub->user.on_rx_notify) {
+ (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text,
+ &res_hdr, &body);
+
+ /* Application MUST specify final response! */
+ PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
+
+ /* Must be a valid status code */
+ PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
+ }
+
+
+ /* If non-2xx should be returned, then send the response.
+ * No need to update server subscription state.
+ */
+ if (st_code >= 300) {
+ status = create_response(sub, rdata, st_code, st_text, &res_hdr,
+ body, &tdata);
+ if (status == PJ_SUCCESS) {
+ status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
+ }
+
+ /* Start timer to terminate subscription, just in case server
+ * is not able to generate NOTIFY to our response.
+ */
+ if (status == PJ_SUCCESS) {
+ unsigned timeout = TIME_UAC_WAIT_NOTIFY;
+ set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout);
+ } else {
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
+ }
+
+ return;
+ }
+
+ /* Update expiration from the value of expires param in
+ * Subscription-State header, but ONLY when subscription state
+ * is "active" or "pending", AND the header contains expires param.
+ */
+ if (sub->expires->ivalue != 0 &&
+ sub_state->expires_param >= 0 &&
+ (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 ||
+ pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0))
+ {
+ next_refresh = sub_state->expires_param;
+
+ } else {
+ next_refresh = sub->expires->ivalue;
+ }
+
+ /* Update time */
+ update_expires(sub, next_refresh);
+
+ /* Start UAC refresh timer, only when we're not unsubscribing */
+ if (sub->expires->ivalue != 0) {
+ unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ?
+ next_refresh - TIME_UAC_REFRESH : next_refresh;
+
+ PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout));
+ set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout);
+ }
+
+ /* Find out the state */
+ get_hdr_state(sub_state, &new_state, &new_state_str);
+
+ /* Send response. */
+ status = create_response(sub, rdata, st_code, st_text, &res_hdr,
+ body, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
+
+ /* Set the state */
+ if (status == PJ_SUCCESS) {
+ set_state(sub, new_state, new_state_str, event);
+ } else {
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event);
+ }
+
+
+ } else {
+
+ /*
+ * Unexpected method!
+ */
+ PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
+ (int)tsx->method.name.slen, tsx->method.name.ptr));
+ }
+}
+
+
+/*
+ * Transaction event processing by UAS, after subscription is accepted.
+ */
+static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+
+ if (pjsip_method_cmp(&tsx->method, &sub->method) == 0) {
+
+ /*
+ * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption.
+ *
+ */
+ pjsip_rx_data *rdata;
+ pjsip_event_hdr *event_hdr;
+ pjsip_expires_hdr *expires;
+ pjsip_msg *msg;
+ pjsip_tx_data *tdata;
+ int st_code = 200;
+ pj_str_t *st_text = NULL;
+ pjsip_hdr res_hdr;
+ pjsip_msg_body *body = NULL;
+ pjsip_evsub_state old_state;
+ pj_str_t old_state_str;
+ pj_status_t status;
+
+
+ /* Only wants to handle the first event when the request is
+ * received.
+ */
+ if (tsx->state != PJSIP_TSX_STATE_TRYING)
+ return;
+
+ rdata = event->body.tsx_state.src.rdata;
+ msg = rdata->msg_info.msg;
+
+ /* Set expiration time based on client request (in Expires header),
+ * or package default expiration time.
+ */
+ event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL);
+ expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL);
+ if (event_hdr && expires) {
+ struct evpkg *evpkg;
+
+ evpkg = find_pkg(&event_hdr->event_type);
+ if (evpkg) {
+ if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires)
+ sub->expires->ivalue = expires->ivalue;
+ else
+ sub->expires->ivalue = evpkg->pkg_expires;
+ }
+ }
+
+ /* Update time (before calling on_rx_refresh, since application
+ * will send NOTIFY.
+ */
+ update_expires(sub, sub->expires->ivalue);
+
+
+ /* Save old state.
+ * If application respond with non-2xx, revert to old state.
+ */
+ old_state = sub->state;
+ old_state_str = sub->state_str;
+
+ if (sub->expires->ivalue == 0) {
+ sub->state = PJSIP_EVSUB_STATE_TERMINATED;
+ sub->state_str = evsub_state_names[sub->state];
+ } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
+ sub->state = PJSIP_EVSUB_STATE_ACCEPTED;
+ sub->state_str = evsub_state_names[sub->state];
+ }
+
+ /* Call application's on_rx_refresh, just in case it wants to send
+ * response other than 200 (OK)
+ */
+ pj_list_init(&res_hdr);
+
+ (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text,
+ &res_hdr, &body);
+
+ /* Application MUST specify final response! */
+ PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; });
+
+ /* Must be a valid status code */
+ PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; });
+
+
+ /* Create and send response */
+ status = create_response(sub, rdata, st_code, st_text, &res_hdr,
+ body, &tdata);
+ if (status == PJ_SUCCESS) {
+ /* Add expires header: */
+ pjsip_msg_add_hdr( tdata->msg,
+ pjsip_hdr_shallow_clone(tdata->pool,
+ sub->expires));
+
+ /* Send */
+ status = pjsip_dlg_send_response(sub->dlg, tsx, tdata);
+ }
+
+ /* Update state or revert state */
+ if (st_code/100==2) {
+
+ if (sub->expires->ivalue == 0) {
+ set_state(sub, sub->state, NULL, event);
+ } else if (sub->state == PJSIP_EVSUB_STATE_NULL) {
+ set_state(sub, sub->state, NULL, event);
+ }
+
+ /* Set UAS timeout timer, when state is not terminated. */
+ if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) {
+ PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds",
+ sub->expires->ivalue));
+ set_timer(sub, TIMER_TYPE_UAS_TIMEOUT,
+ sub->expires->ivalue);
+ }
+
+ } else {
+ sub->state = old_state;
+ sub->state_str = old_state_str;
+ }
+
+
+ } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) {
+
+ /* Handle authentication */
+ if (tsx->state == PJSIP_TSX_STATE_COMPLETED &&
+ (tsx->status_code==401 || tsx->status_code==407))
+ {
+ pjsip_rx_data *rdata = event->body.tsx_state.src.rdata;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata,
+ tsx->last_tx, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_dlg_send_request( sub->dlg, tdata, NULL );
+
+ if (status != PJ_SUCCESS) {
+ /* Can't authenticate. Terminate session (?) */
+ set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL);
+ }
+ }
+
+ } else {
+
+ /*
+ * Unexpected method!
+ */
+ PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s",
+ (int)tsx->method.name.slen, tsx->method.name.ptr));
+
+ }
+}
+
+
+/*
+ * Notification when transaction state has changed!
+ */
+static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
+{
+ pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx);
+
+ if (sub == NULL) {
+ sub = on_new_transaction(tsx, event);
+ if (sub == NULL)
+ return;
+ }
+
+
+ /* Call on_tsx_state callback, if any. */
+ if (sub->user.on_tsx_state)
+ (*sub->user.on_tsx_state)(sub, tsx, event);
+
+
+ /* Process the event: */
+
+ if (sub->role == PJSIP_ROLE_UAC) {
+ on_tsx_state_uac(sub, tsx, event);
+ } else {
+ on_tsx_state_uas(sub, tsx, event);
+ }
+
+
+ /* Check transaction TERMINATE event */
+ if (tsx->state == PJSIP_TSX_STATE_TERMINATED) {
+
+ --sub->pending_tsx;
+
+ if (sub->state == PJSIP_EVSUB_STATE_TERMINATED &&
+ sub->pending_tsx == 0)
+ {
+ evsub_destroy(sub);
+ }
+
+ }
+}
+
+
diff --git a/pjsip/src/pjsip-simple/event_notify_msg.c b/pjsip/src/pjsip-simple/evsub_msg.c
index a5f946ef..7fd44d32 100644
--- a/pjsip/src/pjsip-simple/event_notify_msg.c
+++ b/pjsip/src/pjsip-simple/evsub_msg.c
@@ -16,13 +16,16 @@
* 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_simple/event_notify_msg.h>
-#include <pjsip/print.h>
+#include <pjsip-simple/evsub_msg.h>
+#include <pjsip/print_util.h>
#include <pjsip/sip_parser.h>
#include <pj/pool.h>
#include <pj/string.h>
#include <pj/except.h>
+/*
+ * Event header.
+ */
static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
char *buf, pj_size_t size);
static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
@@ -40,12 +43,15 @@ static pjsip_hdr_vptr event_hdr_vptr =
PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool)
{
- pj_str_t event = { "Event", 5 };
- pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ pjsip_event_hdr *hdr = pj_pool_zalloc(pool, sizeof(*hdr));
hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = event;
+ hdr->name.ptr = "Event";
+ hdr->name.slen = 5;
+ hdr->sname.ptr = "o";
+ hdr->sname.slen = 1;
hdr->vptr = &event_hdr_vptr;
pj_list_init(hdr);
+ pj_list_init(&hdr->other_param);
return hdr;
}
@@ -62,8 +68,14 @@ static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,
copy_advance(p, hdr->event_type);
copy_advance_pair(p, ";id=", 4, hdr->id_param);
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
+
+ printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p,
+ &pjsip_PARAM_CHAR_SPEC,
+ &pjsip_PARAM_CHAR_SPEC, ';');
+ if (printed < 0)
+ return printed;
+
+ p += printed;
return p - buf;
}
@@ -73,93 +85,43 @@ static pjsip_event_hdr* pjsip_event_hdr_clone( pj_pool_t *pool,
pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
pj_strdup(pool, &hdr->event_type, &rhs->event_type);
pj_strdup(pool, &hdr->id_param, &rhs->id_param);
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
return hdr;
}
-static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
- const pjsip_event_hdr *rhs )
+static pjsip_event_hdr*
+pjsip_event_hdr_shallow_clone( pj_pool_t *pool,
+ const pjsip_event_hdr *rhs )
{
pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
return hdr;
}
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *hdr);
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr*);
-
-static pjsip_hdr_vptr allow_event_hdr_vptr =
-{
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone,
- (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone,
- (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print,
-};
-
-
+/*
+ * Allow-Events header.
+ */
PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool)
{
- pj_str_t allow_events = { "Allow-Events", 12 };
- pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
- hdr->type = PJSIP_H_OTHER;
- hdr->name = hdr->sname = allow_events;
- hdr->vptr = &allow_event_hdr_vptr;
- pj_list_init(hdr);
- return hdr;
-}
-
-static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr,
- char *buf, pj_size_t size)
-{
- char *p = buf;
- char *endbuf = buf+size;
- int printed;
+ const pj_str_t STR_ALLOW_EVENTS = { "Allow-Events", 12};
+ pjsip_allow_events_hdr *hdr;
- copy_advance(p, hdr->name);
- *p++ = ':';
- *p++ = ' ';
+ hdr = pjsip_generic_array_hdr_create(pool, &STR_ALLOW_EVENTS);
- if (hdr->event_cnt > 0) {
- int i;
- copy_advance(p, hdr->events[0]);
- for (i=1; i<hdr->event_cnt; ++i) {
- copy_advance_pair(p, ",", 1, hdr->events[i]);
- }
+ if (hdr) {
+ hdr->sname.ptr = "u";
+ hdr->sname.slen = 1;
}
- return p - buf;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- int i;
-
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
- hdr->event_cnt = rhs->event_cnt;
- for (i=0; i<rhs->event_cnt; ++i) {
- pj_strdup(pool, &hdr->events[i], &rhs->events[i]);
- }
- return hdr;
-}
-
-static pjsip_allow_events_hdr*
-pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool,
- const pjsip_allow_events_hdr *rhs)
-{
- pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
- pj_memcpy(hdr, rhs, sizeof(*hdr));
return hdr;
}
+/*
+ * Subscription-State header.
+ */
static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
char *buf, pj_size_t size);
static pjsip_sub_state_hdr*
@@ -180,13 +142,14 @@ static pjsip_hdr_vptr sub_state_hdr_vptr =
PJ_DEF(pjsip_sub_state_hdr*) pjsip_sub_state_hdr_create(pj_pool_t *pool)
{
pj_str_t sub_state = { "Subscription-State", 18 };
- pjsip_sub_state_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr));
+ pjsip_sub_state_hdr *hdr = pj_pool_zalloc(pool, sizeof(*hdr));
hdr->type = PJSIP_H_OTHER;
hdr->name = hdr->sname = sub_state;
hdr->vptr = &sub_state_hdr_vptr;
hdr->expires_param = -1;
hdr->retry_after = -1;
pj_list_init(hdr);
+ pj_list_init(&hdr->other_param);
return hdr;
}
@@ -215,8 +178,15 @@ static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,
printed = pj_utoa(hdr->retry_after, p);
p += printed;
}
- if (hdr->other_param.slen)
- copy_advance(p, hdr->other_param);
+
+ printed = pjsip_param_print_on( &hdr->other_param, p, endbuf-p,
+ &pjsip_PARAM_CHAR_SPEC,
+ &pjsip_PARAM_CHAR_SPEC,
+ ';');
+ if (printed < 0)
+ return printed;
+
+ p += printed;
return p - buf;
}
@@ -230,7 +200,7 @@ pjsip_sub_state_hdr_clone(pj_pool_t *pool,
pj_strdup(pool, &hdr->reason_param, &rhs->reason_param);
hdr->retry_after = rhs->retry_after;
hdr->expires_param = rhs->expires_param;
- pj_strdup(pool, &hdr->other_param, &rhs->other_param);
+ pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param);
return hdr;
}
@@ -240,83 +210,87 @@ pjsip_sub_state_hdr_shallow_clone(pj_pool_t *pool,
{
pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr));
pj_memcpy(hdr, rhs, sizeof(*hdr));
+ pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param);
return hdr;
}
-static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner,
- pj_pool_t *pool)
+
+/*
+ * Parse Event header.
+ */
+static pjsip_hdr *parse_hdr_event(pjsip_parse_ctx *ctx)
{
- pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool);
+ pjsip_event_hdr *hdr = pjsip_event_hdr_create(ctx->pool);
const pj_str_t id_param = { "id", 2 };
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->event_type);
+ pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->event_type);
- while (*scanner->current == ';') {
+ while (*ctx->scanner->curptr == ';') {
pj_str_t pname, pvalue;
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+
+ pj_scan_get_char(ctx->scanner);
+ pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
+
if (pj_stricmp(&pname, &id_param)==0) {
hdr->id_param = pvalue;
} else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ pjsip_param *param = pj_pool_alloc(ctx->pool, sizeof(pjsip_param));
+ param->name = pname;
+ param->value = pvalue;
+ pj_list_push_back(&hdr->other_param, param);
}
}
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
+ pjsip_parse_end_hdr_imp( ctx->scanner );
+ return (pjsip_hdr*)hdr;
}
-static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner,
- pj_pool_t *pool)
-{
- pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool);
-
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]);
- hdr->event_cnt = 1;
-
- while (*scanner->current == ',') {
- pj_scan_get_char(scanner);
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]);
- if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) {
- PJ_THROW(PJSIP_SYN_ERR_EXCEPTION);
- }
- }
-
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
-}
-
-static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner,
- pj_pool_t *pool)
+/*
+ * Parse Subscription-State header.
+ */
+static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx )
{
- pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool);
+ pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(ctx->pool);
const pj_str_t reason = { "reason", 6 },
expires = { "expires", 7 },
retry_after = { "retry-after", 11 };
- pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->sub_state);
+ pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->sub_state);
- while (*scanner->current == ';') {
+ while (*ctx->scanner->curptr == ';') {
pj_str_t pname, pvalue;
- pj_scan_get_char(scanner);
- pjsip_parse_param_imp(scanner, &pname, &pvalue, 0);
+ pj_scan_get_char(ctx->scanner);
+ pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0);
+
if (pj_stricmp(&pname, &reason) == 0) {
hdr->reason_param = pvalue;
+
} else if (pj_stricmp(&pname, &expires) == 0) {
hdr->expires_param = pj_strtoul(&pvalue);
+
} else if (pj_stricmp(&pname, &retry_after) == 0) {
hdr->retry_after = pj_strtoul(&pvalue);
+
} else {
- pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';');
+ pjsip_param *param = pj_pool_alloc(ctx->pool, sizeof(pjsip_param));
+ param->name = pname;
+ param->value = pvalue;
+ pj_list_push_back(&hdr->other_param, param);
}
}
- pjsip_parse_end_hdr_imp( scanner );
- return hdr;
+ pjsip_parse_end_hdr_imp( ctx->scanner );
+ return (pjsip_hdr*)hdr;
}
-PJ_DEF(void) pjsip_event_notify_init_parser(void)
+/*
+ * Register header parsers.
+ */
+PJ_DEF(void) pjsip_evsub_init_parser(void)
{
- pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event);
- pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events);
- pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state);
+ pjsip_register_hdr_parser( "Event", NULL,
+ &parse_hdr_event);
+
+ pjsip_register_hdr_parser( "Subscription-State", NULL,
+ &parse_hdr_sub_state);
}
+
diff --git a/pjsip/src/pjsip-simple/messaging.c b/pjsip/src/pjsip-simple/messaging.c
deleted file mode 100644
index 6e08af68..00000000
--- a/pjsip/src/pjsip-simple/messaging.c
+++ /dev/null
@@ -1,352 +0,0 @@
-/* $Id$ */
-/*
- * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * 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_simple/messaging.h>
-#include <pjsip/sip_endpoint.h>
-#include <pjsip/sip_parser.h>
-#include <pjsip/sip_transaction.h>
-#include <pjsip/sip_event.h>
-#include <pjsip/sip_module.h>
-#include <pjsip/sip_util.h>
-#include <pj/string.h>
-#include <pj/pool.h>
-#include <pj/guid.h>
-#include <pj/string.h>
-#include <pj/log.h>
-#include <stdio.h>
-#include <stdlib.h>
-
-#define THIS_FILE "messaging"
-
-struct messaging_data
-{
- void *token;
- pjsip_messaging_cb cb;
-};
-
-struct pjsip_messaging_session
-{
- pj_pool_t *pool;
- pjsip_endpoint *endpt;
- pjsip_from_hdr *from;
- pjsip_to_hdr *to;
- pjsip_cid_hdr *call_id;
- pjsip_cseq_hdr *cseq;
-};
-
-static int module_id;
-static pjsip_on_new_msg_cb incoming_cb;
-static pjsip_method message_method;
-
-
-/*
- * Set global callback to receive incoming message.
- */
-PJ_DEF(pjsip_on_new_msg_cb)
-pjsip_messaging_set_incoming_callback(pjsip_on_new_msg_cb cb)
-{
- pjsip_on_new_msg_cb prev_cb = incoming_cb;
- incoming_cb = cb;
- return prev_cb;
-}
-
-
-/*
- * Create an independent message (ie. not associated with a session).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg_from_hdr(pjsip_endpoint *endpt,
- const pjsip_uri *target,
- const pjsip_from_hdr *param_from,
- const pjsip_to_hdr *param_to,
- const pjsip_cid_hdr *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request_from_hdr( endpt, &message_method,
- target,
- param_from, param_to,
- NULL, param_call_id,
- param_cseq, param_text );
-}
-
-/*
- * Create independent message from string (instead of from header).
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_create_msg( pjsip_endpoint *endpt,
- const pj_str_t *target,
- const pj_str_t *param_from,
- const pj_str_t *param_to,
- const pj_str_t *param_call_id,
- int param_cseq,
- const pj_str_t *param_text)
-{
- return pjsip_endpt_create_request( endpt, &message_method, target,
- param_from, param_to, NULL, param_call_id,
- param_cseq, param_text);
-}
-
-/*
- * Initiate transaction to send outgoing message.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_send_msg( pjsip_endpoint *endpt, pjsip_tx_data *tdata,
- void *token, pjsip_messaging_cb cb )
-{
- pjsip_transaction *tsx;
- struct messaging_data *msg_data;
-
- /* Create transaction. */
- tsx = pjsip_endpt_create_tsx(endpt);
- if (!tsx) {
- pjsip_tx_data_dec_ref(tdata);
- return -1;
- }
-
- /* Save parameters to messaging data and attach to tsx. */
- msg_data = pj_pool_calloc(tsx->pool, 1, sizeof(struct messaging_data));
- msg_data->cb = cb;
- msg_data->token = token;
-
- /* Init transaction. */
- tsx->module_data[module_id] = msg_data;
- if (pjsip_tsx_init_uac(tsx, tdata) != 0) {
- pjsip_tx_data_dec_ref(tdata);
- pjsip_endpt_destroy_tsx(endpt, tsx);
- return -1;
- }
-
- pjsip_endpt_register_tsx(endpt, tsx);
-
- /*
- * Instruct transaction to send message.
- * Further events will be received via transaction's event.
- */
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- /* Decrement reference counter. */
- pjsip_tx_data_dec_ref(tdata);
- return 0;
-}
-
-
-/*
- * Create 'IM session'.
- */
-PJ_DEF(pjsip_messaging_session*)
-pjsip_messaging_create_session( pjsip_endpoint *endpt, const pj_str_t *param_from,
- const pj_str_t *param_to )
-{
- pj_pool_t *pool;
- pjsip_messaging_session *ses;
- pj_str_t tmp, to;
-
- pool = pjsip_endpt_create_pool(endpt, "imsess", 1024, 1024);
- if (!pool)
- return NULL;
-
- ses = pj_pool_calloc(pool, 1, sizeof(pjsip_messaging_session));
- ses->pool = pool;
- ses->endpt = endpt;
-
- ses->call_id = pjsip_cid_hdr_create(pool);
- pj_create_unique_string(pool, &ses->call_id->id);
-
- ses->cseq = pjsip_cseq_hdr_create(pool);
- ses->cseq->cseq = pj_rand();
- ses->cseq->method = message_method;
-
- ses->from = pjsip_from_hdr_create(pool);
- pj_strdup_with_null(pool, &tmp, param_from);
- ses->from->uri = pjsip_parse_uri(pool, tmp.ptr, tmp.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->from->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
- pj_create_unique_string(pool, &ses->from->tag);
-
- ses->to = pjsip_to_hdr_create(pool);
- pj_strdup_with_null(pool, &to, param_from);
- ses->to->uri = pjsip_parse_uri(pool, to.ptr, to.slen, PJSIP_PARSE_URI_AS_NAMEADDR);
- if (ses->to->uri == NULL) {
- pjsip_endpt_destroy_pool(endpt, pool);
- return NULL;
- }
-
- PJ_LOG(4,(THIS_FILE, "IM session created: recipient=%s", to.ptr));
- return ses;
-}
-
-
-/*
- * Send IM message using identification from 'IM session'.
- */
-PJ_DEF(pjsip_tx_data*)
-pjsip_messaging_session_create_msg( pjsip_messaging_session *ses, const pj_str_t *text )
-{
- return pjsip_endpt_create_request_from_hdr( ses->endpt,
- &message_method,
- ses->to->uri,
- ses->from,
- ses->to,
- NULL,
- ses->call_id,
- ses->cseq->cseq++,
- text);
-}
-
-
-/*
- * Destroy 'IM session'.
- */
-PJ_DEF(pj_status_t)
-pjsip_messaging_destroy_session( pjsip_messaging_session *ses )
-{
- /*
- * NOTE ABOUT POSSIBLE BUG HERE...
- *
- * We don't check number of pending transaction before destroying IM
- * session. As the result, the headers in the txdata of pending transaction
- * wil be INVALID once the IM session is deleted (because we only
- * shallo_clone()-ed them).
- *
- * This normally should be okay, because once the message is
- * submitted to transaction, the transaction (or rather the transport)
- * will 'print' the message to a buffer, and once it is printed, it
- * won't try to access the original message again. So even when the
- * original message has a dangling pointer, we should be safe.
- *
- * However, it will cause a problem if:
- * - resolving completes asynchronously and with a substantial delay,
- * and before the resolver/transport finished its job the user
- * destroy the IM session.
- * - if the transmit data is invalidated after the IM session is
- * destroyed.
- */
-
- pjsip_endpt_destroy_pool(ses->endpt, ses->pool);
- return 0;
-}
-
-
-static pj_status_t messaging_init( pjsip_endpoint *endpt,
- struct pjsip_module *mod, pj_uint32_t id )
-{
- PJ_UNUSED_ARG(endpt)
- PJ_UNUSED_ARG(mod)
-
- module_id = id;
- return 0;
-}
-
-static pj_status_t messaging_start( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static pj_status_t messaging_deinit( struct pjsip_module *mod )
-{
- PJ_UNUSED_ARG(mod)
- return 0;
-}
-
-static void messaging_tsx_handler( struct pjsip_module *mod, pjsip_event *event )
-{
- pjsip_transaction *tsx = event->obj.tsx;
- struct messaging_data *mod_data;
-
- PJ_UNUSED_ARG(mod)
-
- /* Ignore non transaction event */
- if (event->type != PJSIP_EVENT_TSX_STATE_CHANGED || tsx == NULL)
- return;
-
- /* If this is an incoming message, inform application. */
- if (tsx->role == PJSIP_ROLE_UAS) {
- int status = 100;
- pjsip_tx_data *tdata;
-
- /* Check if we already answered this request. */
- if (tsx->status_code >= 200)
- return;
-
- /* Only handle MESSAGE requests!. */
- if (pjsip_method_cmp(&tsx->method, &message_method) != 0)
- return;
-
- /* Call application callback. */
- if (incoming_cb)
- status = (*incoming_cb)(event->src.rdata);
-
- if (status < 200 || status >= 700)
- status = PJSIP_SC_INTERNAL_SERVER_ERROR;
-
- /* Respond request. */
- tdata = pjsip_endpt_create_response(tsx->endpt, event->src.rdata, status );
- if (tdata)
- pjsip_tsx_on_tx_msg(tsx, tdata);
-
- return;
- }
-
- /* Ignore if it's not something that came from messaging module. */
- mod_data = tsx->module_data[ module_id ];
- if (mod_data == NULL)
- return;
-
- /* Ignore non final response. */
- if (tsx->status_code < 200)
- return;
-
- /* Don't want to call the callback more than once. */
- tsx->module_data[ module_id ] = NULL;
-
- /* Now call the callback. */
- if (mod_data->cb) {
- (*mod_data->cb)(mod_data->token, tsx->status_code);
- }
-}
-
-static pjsip_module messaging_module =
-{
- { "Messaging", 9}, /* Name. */
- 0, /* Flag */
- 128, /* Priority */
- NULL, /* User agent instance, initialized by APP. */
- 0, /* Number of methods supported (will be initialized later). */
- { 0 }, /* Array of methods (will be initialized later) */
- &messaging_init, /* init_module() */
- &messaging_start, /* start_module() */
- &messaging_deinit, /* deinit_module() */
- &messaging_tsx_handler, /* tsx_handler() */
-};
-
-PJ_DEF(pjsip_module*) pjsip_messaging_get_module()
-{
- static pj_str_t method_str = { "MESSAGE", 7 };
-
- pjsip_method_init_np( &message_method, &method_str);
-
- messaging_module.method_cnt = 1;
- messaging_module.methods[0] = &message_method;
-
- return &messaging_module;
-}
-
diff --git a/pjsip/src/pjsip-simple/pidf.c b/pjsip/src/pjsip-simple/pidf.c
index 7ababcad..acd20092 100644
--- a/pjsip/src/pjsip-simple/pidf.c
+++ b/pjsip/src/pjsip-simple/pidf.c
@@ -16,9 +16,11 @@
* 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_simple/pidf.h>
+#include <pjsip-simple/pidf.h>
#include <pj/string.h>
#include <pj/pool.h>
+#include <pj/assert.h>
+
struct pjpidf_op_desc pjpidf_op =
{
@@ -132,7 +134,7 @@ PJ_DEF(pjpidf_tuple*) pjpidf_pres_find_tuple(pjpidf_pres *pres, const pj_str_t *
PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t)
{
- PJ_UNUSED_ARG(pres)
+ PJ_UNUSED_ARG(pres);
pj_list_erase(t);
}
diff --git a/pjsip/src/pjsip-simple/presence.c b/pjsip/src/pjsip-simple/presence.c
index a9cc6108..ca033f5b 100644
--- a/pjsip/src/pjsip-simple/presence.c
+++ b/pjsip/src/pjsip-simple/presence.c
@@ -16,384 +16,919 @@
* 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_simple/presence.h>
-#include <pjsip/sip_transport.h>
-#include <pj/pool.h>
-#include <pj/string.h>
+#include <pjsip-simple/presence.h>
+#include <pjsip-simple/errno.h>
+#include <pjsip-simple/evsub_msg.h>
+#include <pjsip/sip_module.h>
+#include <pjsip/sip_endpoint.h>
+#include <pjsip/sip_dialog.h>
+#include <pj/assert.h>
#include <pj/guid.h>
+#include <pj/log.h>
#include <pj/os.h>
-#include <stdio.h>
-
-/* Forward declarations. */
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status);
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **cb, int *expires);
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason);
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata);
-
-/* Some string constants. */
-static pj_str_t PRESENCE_EVENT = { "presence", 8 };
-
-/* Accept types. */
-static pj_str_t accept_names[] = {
- { "application/pidf+xml", 20 },
- { "application/xpidf+xml", 21 }
+#include <pj/pool.h>
+#include <pj/string.h>
+
+
+#define THIS_FILE "presence.c"
+#define PRES_DEFAULT_EXPIRES 600
+
+/*
+ * Presence module (mod-presence)
+ */
+static struct pjsip_module mod_presence =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-presence", 12 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION-1, /* 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() */
+ NULL, /* on_tsx_state() */
};
-static pjsip_media_type accept_types[] = {
- {
- { "application", 11 },
- { "pidf+xml", 8 }
- },
- {
- { "application", 11 },
- { "xpidf+xml", 9 }
- }
+
+
+/*
+ * Presence message body type.
+ */
+typedef enum content_type
+{
+ CONTENT_TYPE_NONE,
+ CONTENT_TYPE_PIDF,
+ CONTENT_TYPE_XPIDF,
+} content_type;
+
+/*
+ * This structure describe a presentity, for both subscriber and notifier.
+ */
+struct pjsip_pres
+{
+ pjsip_evsub *sub; /**< Event subscribtion record. */
+ pjsip_dialog *dlg; /**< The dialog. */
+ content_type content_type; /**< Content-Type. */
+ pjsip_pres_status status; /**< Presence status. */
+ pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/
+ pjsip_evsub_user user_cb; /**< The user callback. */
};
-/* Callback that is registered by application. */
-static pjsip_presence_cb cb;
-/* Package callback to be register to event_notify */
-static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe,
- &on_subscribe };
+typedef struct pjsip_pres pjsip_pres;
-/* Global/static callback to be registered to event_notify */
-static pjsip_event_sub_cb sub_cb = { &on_sub_terminated,
- &on_sub_received_refresh,
- NULL,
- &on_received_notify,
- NULL };
/*
- * Initialize presence module.
- * This will register event package "presence" to event framework.
+ * Forward decl for evsub callback.
*/
-PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb)
+static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
+static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
+ pjsip_event *event);
+static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body);
+static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body);
+static void pres_on_evsub_client_refresh(pjsip_evsub *sub);
+static void pres_on_evsub_server_timeout(pjsip_evsub *sub);
+
+
+/*
+ * Event subscription callback for presence.
+ */
+static pjsip_evsub_user pres_user =
{
- pj_memcpy(&cb, pcb, sizeof(*pcb));
- pjsip_event_sub_register_pkg( &PRESENCE_EVENT,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- &pkg_cb);
+ &pres_on_evsub_state,
+ &pres_on_evsub_tsx_state,
+ &pres_on_evsub_rx_refresh,
+ &pres_on_evsub_rx_notify,
+ &pres_on_evsub_client_refresh,
+ &pres_on_evsub_server_timeout,
+};
+
+
+/*
+ * Some static constants.
+ */
+const pj_str_t STR_EVENT = { "Event", 5 };
+const pj_str_t STR_PRESENCE = { "presence", 8 };
+const pj_str_t STR_APPLICATION = { "application", 11 };
+const pj_str_t STR_PIDF_XML = { "pidf+xml", 8};
+const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9};
+const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 };
+const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 };
+
+
+/*
+ * Init presence module.
+ */
+PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt,
+ pjsip_module *mod_evsub)
+{
+ pj_status_t status;
+ pj_str_t accept[2];
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL);
+
+ /* Must have not been registered */
+ PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP);
+
+ /* Register to endpoint */
+ status = pjsip_endpt_register_module(endpt, &mod_presence);
+ if (status != PJ_SUCCESS)
+ return status;
+
+ accept[0] = STR_APP_PIDF_XML;
+ accept[1] = STR_APP_XPIDF_XML;
+
+ /* Register event package to event module. */
+ status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE,
+ PRES_DEFAULT_EXPIRES,
+ PJ_ARRAY_SIZE(accept), accept);
+ if (status != PJ_SUCCESS) {
+ pjsip_endpt_unregister_module(endpt, &mod_presence);
+ return status;
+ }
+
+ return PJ_SUCCESS;
}
+
+/*
+ * Get presence module instance.
+ */
+PJ_DEF(pjsip_module*) pjsip_pres_instance(void)
+{
+ return &mod_presence;
+}
+
+
/*
- * Create presence subscription.
+ * Create client subscription.
*/
-PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt,
- const pj_str_t *local_url,
- const pj_str_t *remote_url,
- int expires,
- void *user_data )
+PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_evsub **p_evsub )
{
- pjsip_event_sub *sub;
- pjsip_presentity *pres;
+ pj_status_t status;
+ pjsip_pres *pres;
+ pjsip_evsub *sub;
- if (expires < 0)
- expires = 300;
+ PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL);
+
+ pjsip_dlg_inc_lock(dlg);
/* Create event subscription */
- sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT,
- expires,
- sizeof(accept_names)/sizeof(accept_names[0]),
- accept_names,
- NULL, &sub_cb);
- if (!sub)
- return NULL;
+ status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, &sub);
+ if (status != PJ_SUCCESS)
+ goto on_return;
- /* Allocate presence descriptor. */
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->user_data = user_data;
- sub->user_data = pres;
+ /* Create presence */
+ pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
+ pres->dlg = dlg;
+ if (user_cb)
+ pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
+
+ /* Attach to evsub */
+ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
- return pres;
+ *p_evsub = sub;
+
+on_return:
+ pjsip_dlg_dec_lock(dlg);
+ return status;
}
+
/*
- * Send SUBSCRIBE.
+ * Create server subscription.
*/
-PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres )
+PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg,
+ const pjsip_evsub_user *user_cb,
+ pjsip_rx_data *rdata,
+ pjsip_evsub **p_evsub )
{
- return pjsip_event_sub_subscribe( pres->sub );
+ pjsip_accept_hdr *accept;
+ pjsip_event_hdr *event;
+ pjsip_expires_hdr *expires_hdr;
+ unsigned expires;
+ content_type content_type = CONTENT_TYPE_NONE;
+ pjsip_evsub *sub;
+ pjsip_pres *pres;
+ pj_status_t status;
+
+ /* Check arguments */
+ PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL);
+
+ /* Must be request message */
+ PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG,
+ PJSIP_ENOTREQUESTMSG);
+
+ /* Check that request is SUBSCRIBE */
+ PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method,
+ &pjsip_subscribe_method)==0,
+ PJSIP_SIMPLE_ENOTSUBSCRIBE);
+
+ /* Check that Event header contains "presence" */
+ event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL);
+ if (!event) {
+ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST);
+ }
+ if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) {
+ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT);
+ }
+
+ /* Check that request contains compatible Accept header. */
+ accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL);
+ if (accept) {
+ unsigned i;
+ for (i=0; i<accept->count; ++i) {
+ if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) {
+ content_type = CONTENT_TYPE_PIDF;
+ break;
+ } else
+ if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) {
+ content_type = CONTENT_TYPE_XPIDF;
+ break;
+ }
+ }
+
+ if (i==accept->count) {
+ /* Nothing is acceptable */
+ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE);
+ }
+
+ } else {
+ /* No Accept header.
+ * Treat as "application/pidf+xml"
+ */
+ content_type = CONTENT_TYPE_PIDF;
+ }
+
+ /* Check that expires is not too short. */
+ expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL);
+ if (expires_hdr) {
+ if (expires_hdr->ivalue < 5) {
+ return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF);
+ }
+
+ expires = expires_hdr->ivalue;
+ if (expires > PRES_DEFAULT_EXPIRES)
+ expires = PRES_DEFAULT_EXPIRES;
+
+ } else {
+ expires = PRES_DEFAULT_EXPIRES;
+ }
+
+ /* Lock dialog */
+ pjsip_dlg_inc_lock(dlg);
+
+
+ /* Create server subscription */
+ status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, &sub);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+ /* Create server presence subscription */
+ pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres));
+ pres->dlg = dlg;
+ pres->sub = sub;
+ pres->content_type = content_type;
+ if (user_cb)
+ pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user));
+
+ /* Attach to evsub */
+ pjsip_evsub_set_mod_data(sub, mod_presence.id, pres);
+
+ /* Done: */
+ *p_evsub = sub;
+
+on_return:
+ pjsip_dlg_dec_lock(dlg);
+ return status;
}
+
/*
- * Set credentials to be used for outgoing requests.
+ * Create SUBSCRIBE
*/
-PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres,
- int count,
- const pjsip_cred_info cred[])
+PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub,
+ pj_int32_t expires,
+ pjsip_tx_data **p_tdata)
{
- return pjsip_event_sub_set_credentials(pres->sub, count, cred);
+ return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires,
+ p_tdata);
}
+
/*
- * Set route-set.
+ * Accept incoming subscription.
*/
-PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres,
- const pjsip_route_hdr *hdr )
+PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int st_code,
+ const pjsip_hdr *hdr_list )
{
- return pjsip_event_sub_set_route_set( pres->sub, hdr );
+ return pjsip_evsub_accept( sub, rdata, st_code, hdr_list );
}
+
/*
- * Unsubscribe.
+ * Get presence status.
*/
-PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres )
+PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub,
+ pjsip_pres_status *status )
{
- return pjsip_event_sub_unsubscribe(pres->sub);
+ pjsip_pres *pres;
+
+ PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
+
+ if (pres->tmp_status._is_valid)
+ pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status));
+ else
+ pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status));
+
+ return PJ_SUCCESS;
}
+
/*
- * This is the pjsip_msg_body callback to print XML body.
+ * Set presence status.
*/
-static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size)
+PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub,
+ const pjsip_pres_status *status )
{
- return pj_xml_print( body->data, buf, size, PJ_TRUE );
+ unsigned i;
+ pjsip_pres *pres;
+
+ PJ_ASSERT_RETURN(sub && status, PJ_EINVAL);
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE);
+
+ for (i=0; i<status->info_cnt; ++i) {
+ pres->status.info[i].basic_open = status->info[i].basic_open;
+ if (status->info[i].id.slen == 0) {
+ pj_create_unique_string(pres->dlg->pool,
+ &pres->status.info[i].id);
+ } else {
+ pj_strdup(pres->dlg->pool,
+ &pres->status.info[i].id,
+ &status->info[i].id);
+ }
+ pj_strdup(pres->dlg->pool,
+ &pres->status.info[i].contact,
+ &status->info[i].contact);
+ }
+
+ pres->status.info_cnt = status->info_cnt;
+
+ return PJ_SUCCESS;
}
+
/*
- * Create and initialize PIDF document and msg body (notifier only).
+ * Create PIDF document based on the presence info.
*/
-static pj_status_t init_presence_info( pjsip_presentity *pres )
+static pjpidf_pres* pres_create_pidf( pj_pool_t *pool,
+ pjsip_pres *pres )
{
- pj_str_t uri;
- pj_pool_t *pool = pres->sub->pool;
- char tmp[PJSIP_MAX_URL_SIZE];
- pjpidf_tuple *tuple;
- const pjsip_media_type *content_type = NULL;
-
- pj_assert(pres->uas_body == NULL);
+ pjpidf_pres *pidf;
+ unsigned i;
+ pj_str_t entity;
+
+ /* Get publisher URI */
+ entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+ entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+ pres->dlg->local.info->uri,
+ entity.ptr, PJSIP_MAX_URL_SIZE);
+ if (entity.slen < 1)
+ return NULL;
- /* Make entity_id */
- uri.ptr = tmp;
- uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri,
- tmp, sizeof(tmp));
- if (uri.slen < 0)
- return -1;
+ /* Create <presence>. */
+ pidf = pjpidf_create(pool, &entity);
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pj_str_t s;
+ /* Create <tuple> */
+ for (i=0; i<pres->status.info_cnt; ++i) {
- /* Create <presence>. */
- pres->uas_data.pidf = pjpidf_create(pool, &s);
+ pjpidf_tuple *pidf_tuple;
+ pjpidf_status *pidf_status;
- /* Create <tuple> */
- pj_create_unique_string(pool, &s);
- tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s);
+ /* Add tuple id. */
+ pidf_tuple = pjpidf_pres_add_tuple(pool, pidf,
+ &pres->status.info[i].id);
/* Set <contact> */
- s.ptr = tmp;
- s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp));
- if (s.slen < 0)
- return -1;
- pjpidf_tuple_set_contact(pool, tuple, &s);
+ if (pres->status.info[i].contact.slen)
+ pjpidf_tuple_set_contact(pool, pidf_tuple,
+ &pres->status.info[i].contact);
- /* Content-Type */
- content_type = &accept_types[PJSIP_PRES_TYPE_PIDF];
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
+ /* Set basic status */
+ pidf_status = pjpidf_tuple_get_status(pidf_tuple);
+ pjpidf_status_set_basic_open(pidf_status,
+ pres->status.info[i].basic_open);
+ }
- /* Create XPIDF */
- pres->uas_data.xpidf = pjxpidf_create(pool, &uri);
+ return pidf;
+}
- /* Content-Type. */
- content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF];
- }
- /* Create message body */
- pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body));
- pres->uas_body->content_type = *content_type;
- pres->uas_body->data = pres->uas_data.pidf;
- pres->uas_body->len = 0;
- pres->uas_body->print_body = &print_xml;
+/*
+ * Create XPIDF document based on the presence info.
+ */
+static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool,
+ pjsip_pres *pres )
+{
+ /* Note: PJSIP implementation of XPIDF is not complete!
+ */
+ pjxpidf_pres *xpidf;
+ pj_str_t publisher_uri;
+
+ PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported "
+ "by PJSIP"));
+
+ publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE);
+ publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+ pres->dlg->local.info->uri,
+ publisher_uri.ptr,
+ PJSIP_MAX_URL_SIZE);
+ if (publisher_uri.slen < 1)
+ return NULL;
+
+ /* Create XPIDF document. */
+ xpidf = pjxpidf_create(pool, &publisher_uri);
- return 0;
+ /* Set basic status. */
+ if (pres->status.info_cnt > 0)
+ pjxpidf_set_status( xpidf, pres->status.info[0].basic_open);
+ else
+ pjxpidf_set_status( xpidf, PJ_FALSE);
+
+ return xpidf;
}
+
/*
- * Send NOTIFY and set subscription state.
+ * Function to print XML message body.
*/
-PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres,
- pjsip_event_sub_state state,
- pj_bool_t is_online )
+static int pres_print_body(struct pjsip_msg_body *msg_body,
+ char *buf, pj_size_t size)
{
- pj_str_t reason = { "", 0 };
+ return pj_xml_print(msg_body->data, buf, size, PJ_TRUE);
+}
- if (pres->uas_data.pidf == NULL) {
- if (init_presence_info(pres) != 0)
- return -1;
- }
- /* Update basic status in PIDF/XPIDF document. */
- if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) {
- pjpidf_tuple *first;
- pjpidf_status *status;
- pj_time_val now;
- pj_parsed_time pnow;
-
- first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf);
- pj_assert(first);
- status = pjpidf_op.tuple.get_status(first);
- pj_assert(status);
- pjpidf_op.status.set_basic_open(status, is_online);
-
- /* Update timestamp. */
- if (pres->timestamp.ptr == 0) {
- pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24);
- }
- pj_gettimeofday(&now);
- pj_time_decode(&now, &pnow);
- pres->timestamp.slen = sprintf(pres->timestamp.ptr,
- "%04d-%02d-%02dT%02d:%02d:%02dZ",
- pnow.year, pnow.mon, pnow.day,
- pnow.hour, pnow.min, pnow.sec);
- pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp);
+/*
+ * Function to clone XML document.
+ */
+static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len)
+{
+ PJ_UNUSED_ARG(len);
+ return pj_xml_clone( pool, data);
+}
+
+
+/*
+ * Create message body.
+ */
+static pj_status_t pres_create_msg_body( pjsip_pres *pres,
+ pjsip_tx_data *tdata)
+{
+ pjsip_msg_body *body;
+
+ body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body));
+
+ if (pres->content_type == CONTENT_TYPE_PIDF) {
+
+ body->data = pres_create_pidf(tdata->pool, pres);
+ body->content_type.type = pj_str("application");
+ body->content_type.subtype = pj_str("pidf+xml");
- } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_set_status( pres->uas_data.xpidf, is_online );
+ } else if (pres->content_type == CONTENT_TYPE_XPIDF) {
+
+ body->data = pres_create_xpidf(tdata->pool, pres);
+ body->content_type.type = pj_str("application");
+ body->content_type.subtype = pj_str("xpidf+xml");
} else {
- pj_assert(0);
+ return PJSIP_SIMPLE_EBADCONTENT;
}
- /* Send notify. */
- return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body);
+
+ body->print_body = &pres_print_body;
+ body->clone_data = &xml_clone_data;
+
+ tdata->msg->body = body;
+
+ return PJ_SUCCESS;
}
+
/*
- * Destroy subscription (can be called for both subscriber and notifier).
+ * Create NOTIFY
*/
-PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres )
+PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub,
+ pjsip_evsub_state state,
+ const pj_str_t *state_str,
+ const pj_str_t *reason,
+ pjsip_tx_data **p_tdata)
{
- return pjsip_event_sub_destroy(pres->sub);
+ pjsip_pres *pres;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(sub, PJ_EINVAL);
+
+ /* Get the presence object. */
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
+
+ /* Must have at least one presence info. */
+ PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
+
+
+ /* Lock object. */
+ pjsip_dlg_inc_lock(pres->dlg);
+
+ /* Create the NOTIFY request. */
+ status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Create message body to reflect the presence status. */
+ status = pres_create_msg_body( pres, tdata );
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Done. */
+ *p_tdata = tdata;
+
+
+on_return:
+ pjsip_dlg_dec_lock(pres->dlg);
+ return status;
}
+
+/*
+ * Create NOTIFY that reflect current state.
+ */
+PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub,
+ pjsip_tx_data **p_tdata )
+{
+ pjsip_pres *pres;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ /* Check arguments. */
+ PJ_ASSERT_RETURN(sub, PJ_EINVAL);
+
+ /* Get the presence object. */
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE);
+
+ /* Must have at least one presence info. */
+ PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO);
+
+
+ /* Lock object. */
+ pjsip_dlg_inc_lock(pres->dlg);
+
+ /* Create the NOTIFY request. */
+ status = pjsip_evsub_current_notify( sub, &tdata);
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Create message body to reflect the presence status. */
+ status = pres_create_msg_body( pres, tdata );
+ if (status != PJ_SUCCESS)
+ goto on_return;
+
+
+ /* Done. */
+ *p_tdata = tdata;
+
+
+on_return:
+ pjsip_dlg_dec_lock(pres->dlg);
+ return status;
+}
+
+
+/*
+ * Send request.
+ */
+PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub,
+ pjsip_tx_data *tdata )
+{
+ return pjsip_evsub_send_request(sub, tdata);
+}
+
+
+/*
+ * This callback is called by event subscription when subscription
+ * state has changed.
+ */
+static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event)
+{
+ pjsip_pres *pres;
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
+
+ if (pres->user_cb.on_evsub_state)
+ (*pres->user_cb.on_evsub_state)(sub, event);
+}
+
+/*
+ * Called when transaction state has changed.
+ */
+static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx,
+ pjsip_event *event)
+{
+ pjsip_pres *pres;
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
+
+ if (pres->user_cb.on_tsx_state)
+ (*pres->user_cb.on_tsx_state)(sub, tsx, event);
+}
+
+
/*
- * This callback is called by event framework to query whether we want to
- * accept an incoming subscription.
+ * Called when SUBSCRIBE is received.
*/
-static void on_query_subscribe(pjsip_rx_data *rdata, int *status)
+static void pres_on_evsub_rx_refresh( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body)
{
- if (cb.accept_presence) {
- (*cb.accept_presence)(rdata, status);
+ pjsip_pres *pres;
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
+
+ if (pres->user_cb.on_rx_refresh) {
+ (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text,
+ res_hdr, p_body);
+
+ } else {
+ /* Implementors MUST send NOTIFY if it implements on_rx_refresh */
+ pjsip_tx_data *tdata;
+ pj_str_t timeout = { "timeout", 7};
+ pj_status_t status;
+
+ if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) {
+ status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED,
+ NULL, &timeout, &tdata);
+ } else {
+ status = pjsip_pres_current_notify(sub, &tdata);
+ }
+
+ if (status == PJ_SUCCESS)
+ pjsip_pres_send_request(sub, tdata);
}
}
/*
- * This callback is called by event framework after we accept the incoming
- * subscription, to notify about the new subscription instance.
+ * Parse PIDF to info.
*/
-static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata,
- pjsip_event_sub_cb **set_sub_cb, int *expires)
+static pj_status_t pres_parse_pidf( pjsip_pres *pres,
+ pjsip_rx_data *rdata,
+ pjsip_pres_status *pres_status)
{
- pjsip_presentity *pres;
- pjsip_accept_hdr *accept;
+ pjpidf_pres *pidf;
+ pjpidf_tuple *pidf_tuple;
- pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres));
- pres->sub = sub;
- pres->pres_type = PJSIP_PRES_TYPE_PIDF;
- sub->user_data = pres;
- *set_sub_cb = &sub_cb;
+ pidf = pjpidf_parse(rdata->tp_info.pool,
+ rdata->msg_info.msg->body->data,
+ rdata->msg_info.msg->body->len);
+ if (pidf == NULL)
+ return PJSIP_SIMPLE_EBADPIDF;
- accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL);
- if (accept) {
- unsigned i;
- int found = 0;
- for (i=0; i<accept->count && !found; ++i) {
- int j;
- for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) {
- if (!pj_stricmp(&accept->values[i], &accept_names[j])) {
- pres->pres_type = j;
- found = 1;
- break;
- }
- }
+ pres_status->info_cnt = 0;
+
+ pidf_tuple = pjpidf_pres_get_first_tuple(pidf);
+ while (pidf_tuple) {
+ pjpidf_status *pidf_status;
+
+ pj_strdup(pres->dlg->pool,
+ &pres_status->info[pres_status->info_cnt].id,
+ pjpidf_tuple_get_id(pidf_tuple));
+
+ pj_strdup(pres->dlg->pool,
+ &pres_status->info[pres_status->info_cnt].contact,
+ pjpidf_tuple_get_contact(pidf_tuple));
+
+ pidf_status = pjpidf_tuple_get_status(pidf_tuple);
+ if (pidf_status) {
+ pres_status->info[pres_status->info_cnt].basic_open =
+ pjpidf_status_is_basic_open(pidf_status);
+ } else {
+ pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE;
}
- pj_assert(found );
+
+ pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple );
+ pres_status->info_cnt++;
}
- (*cb.on_received_request)(pres, rdata, expires);
+ return PJ_SUCCESS;
}
/*
- * This callback is called by event framework when the subscription is
- * terminated.
+ * Parse XPIDF info.
*/
-static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason)
+static pj_status_t pres_parse_xpidf( pjsip_pres *pres,
+ pjsip_rx_data *rdata,
+ pjsip_pres_status *pres_status)
{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_terminated)
- (*cb.on_terminated)(pres, reason);
+ pjxpidf_pres *xpidf;
+
+ xpidf = pjxpidf_parse(rdata->tp_info.pool,
+ rdata->msg_info.msg->body->data,
+ rdata->msg_info.msg->body->len);
+ if (xpidf == NULL)
+ return PJSIP_SIMPLE_EBADXPIDF;
+
+ pres_status->info_cnt = 1;
+
+ pj_strdup(pres->dlg->pool,
+ &pres_status->info[0].contact,
+ pjxpidf_get_uri(xpidf));
+ pres_status->info[0].basic_open = pjxpidf_get_status(xpidf);
+ pres_status->info[0].id.slen = 0;
+
+ return PJ_SUCCESS;
}
+
/*
- * This callback is called by event framework when it receives incoming
- * SUBSCRIBE request to refresh the subscription.
+ * Called when NOTIFY is received.
*/
-static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+static void pres_on_evsub_rx_notify( pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body)
{
- pjsip_presentity *pres = sub->user_data;
- if (cb.on_received_refresh)
- (*cb.on_received_refresh)(pres, rdata);
+ pjsip_ctype_hdr *ctype_hdr;
+ pjsip_pres *pres;
+ pj_status_t status;
+
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
+
+ /* Check Content-Type and msg body are present. */
+ ctype_hdr = rdata->msg_info.ctype;
+
+ if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) {
+
+ pjsip_warning_hdr *warn_hdr;
+ pj_str_t warn_text;
+
+ *p_st_code = PJSIP_SC_BAD_REQUEST;
+
+ warn_text = pj_str("Message body is not present");
+ warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399,
+ pjsip_endpt_name(pres->dlg->endpt),
+ &warn_text);
+ pj_list_push_back(res_hdr, warn_hdr);
+
+ return;
+ }
+
+ /* Parse content. */
+
+ if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
+ pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0)
+ {
+ status = pres_parse_pidf( pres, rdata, &pres->tmp_status);
+ }
+ else
+ if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 &&
+ pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0)
+ {
+ status = pres_parse_pidf( pres, rdata, &pres->tmp_status);
+ }
+ else
+ {
+ status = PJSIP_SIMPLE_EBADCONTENT;
+ }
+
+ if (status != PJ_SUCCESS) {
+ /* Unsupported or bad Content-Type */
+ pjsip_accept_hdr *accept_hdr;
+ pjsip_warning_hdr *warn_hdr;
+
+ *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE;
+
+ /* Add Accept header */
+ accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool);
+ accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML;
+ accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML;
+ pj_list_push_back(res_hdr, accept_hdr);
+
+ /* Add Warning header */
+ warn_hdr = pjsip_warning_hdr_create_from_status(
+ rdata->tp_info.pool,
+ pjsip_endpt_name(pres->dlg->endpt),
+ status);
+ pj_list_push_back(res_hdr, warn_hdr);
+
+ return;
+ }
+
+ /* If application calls pres_get_status(), redirect the call to
+ * retrieve the temporary status.
+ */
+ pres->tmp_status._is_valid = PJ_TRUE;
+
+ /* Notify application. */
+ if (pres->user_cb.on_rx_notify) {
+ (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text,
+ res_hdr, p_body);
+ }
+
+
+ /* If application responded NOTIFY with 2xx, copy temporary status
+ * to main status, and mark the temporary status as invalid.
+ */
+ if ((*p_st_code)/100 == 2) {
+ pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status));
+ }
+
+ pres->tmp_status._is_valid = PJ_FALSE;
+
+ /* Done */
}
/*
- * This callback is called by event framework when it receives incoming
- * NOTIFY request.
+ * Called when it's time to send SUBSCRIBE.
*/
-static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata)
+static void pres_on_evsub_client_refresh(pjsip_evsub *sub)
{
- pjsip_presentity *pres = sub->user_data;
+ pjsip_pres *pres;
- if (cb.on_received_update) {
- pj_status_t is_open;
- pjsip_msg_body *body;
- int i;
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
- body = rdata->msg->body;
- if (!body)
- return;
+ if (pres->user_cb.on_client_refresh) {
+ (*pres->user_cb.on_client_refresh)(sub);
+ } else {
+ pj_status_t status;
+ pjsip_tx_data *tdata;
- for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) {
- if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) &&
- !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype))
- {
- break;
- }
- }
+ status = pjsip_pres_initiate(sub, -1, &tdata);
+ if (status == PJ_SUCCESS)
+ pjsip_pres_send_request(sub, tdata);
+ }
+}
- if (i==PJSIP_PRES_TYPE_PIDF) {
- pjpidf_pres *pres;
- pjpidf_tuple *tuple;
- pjpidf_status *status;
-
- pres = pjpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- tuple = pjpidf_pres_get_first_tuple(pres);
- if (!tuple)
- return;
- status = pjpidf_tuple_get_status(tuple);
- if (!status)
- return;
- is_open = pjpidf_status_is_basic_open(status);
-
- } else if (i==PJSIP_PRES_TYPE_XPIDF) {
- pjxpidf_pres *pres;
-
- pres = pjxpidf_parse(rdata->pool, body->data, body->len);
- if (!pres)
- return;
- is_open = pjxpidf_get_status(pres);
+/*
+ * Called when no refresh is received after the interval.
+ */
+static void pres_on_evsub_server_timeout(pjsip_evsub *sub)
+{
+ pjsip_pres *pres;
- } else {
- return;
- }
+ pres = pjsip_evsub_get_mod_data(sub, mod_presence.id);
+ PJ_ASSERT_ON_FAIL(pres!=NULL, {return;});
- (*cb.on_received_update)(pres, is_open);
+ if (pres->user_cb.on_server_timeout) {
+ (*pres->user_cb.on_server_timeout)(sub);
+ } else {
+ pj_status_t status;
+ pjsip_tx_data *tdata;
+ pj_str_t reason = { "timeout", 7 };
+
+ status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED,
+ NULL, &reason, &tdata);
+ if (status == PJ_SUCCESS)
+ pjsip_pres_send_request(sub, tdata);
}
}
diff --git a/pjsip/src/pjsip-simple/xpidf.c b/pjsip/src/pjsip-simple/xpidf.c
index 7cc377ba..12024827 100644
--- a/pjsip/src/pjsip-simple/xpidf.c
+++ b/pjsip/src/pjsip-simple/xpidf.c
@@ -16,10 +16,11 @@
* 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_simple/xpidf.h>
+#include <pjsip-simple/xpidf.h>
+#include <pj/assert.h>
+#include <pj/guid.h>
#include <pj/pool.h>
#include <pj/string.h>
-#include <pj/guid.h>
static pj_str_t PRESENCE = { "presence", 8 };
static pj_str_t STATUS = { "status", 6 };
diff --git a/pjsip/src/pjsip/sip_endpoint.c b/pjsip/src/pjsip/sip_endpoint.c
index e4db5dbb..30d6febe 100644
--- a/pjsip/src/pjsip/sip_endpoint.c
+++ b/pjsip/src/pjsip/sip_endpoint.c
@@ -215,6 +215,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_register_module( pjsip_endpoint *endpt,
/* Done. */
+ PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered",
+ (int)mod->name.slen, mod->name.ptr));
+
on_return:
pj_rwmutex_unlock_write(endpt->mod_mutex);
return status;
@@ -253,6 +256,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
if (status != PJ_SUCCESS) goto on_return;
}
+ /* Module MUST NOT set module ID to -1. */
+ pj_assert(mod->id >= 0);
+
/* Remove module from array. */
endpt->modules[mod->id] = NULL;
@@ -265,6 +271,9 @@ PJ_DEF(pj_status_t) pjsip_endpt_unregister_module( pjsip_endpoint *endpt,
/* Done. */
status = PJ_SUCCESS;
+ PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered",
+ (int)mod->name.slen, mod->name.ptr));
+
on_return:
pj_rwmutex_unlock_write(endpt->mod_mutex);
return status;
diff --git a/pjsip/src/pjsip/sip_errno.c b/pjsip/src/pjsip/sip_errno.c
index a54bf630..d62d9188 100644
--- a/pjsip/src/pjsip/sip_errno.c
+++ b/pjsip/src/pjsip/sip_errno.c
@@ -66,6 +66,7 @@ static const struct
/* Transaction errors */
{ PJSIP_ETSXDESTROYED, "Transaction has been destroyed"},
+ { PJSIP_ENOTSX, "No transaction (expecting stateful processing)" },
/* Authentication. */
{ PJSIP_EFAILEDCREDENTIAL, "Credential failed to authenticate"},
diff --git a/pjsip/src/pjsip/sip_msg.c b/pjsip/src/pjsip/sip_msg.c
index 585c6b42..b3a3438b 100644
--- a/pjsip/src/pjsip/sip_msg.c
+++ b/pjsip/src/pjsip/sip_msg.c
@@ -145,7 +145,7 @@ static int init_status_phrase()
pj_strset2( &status_phrase[486], "Busy Here");
pj_strset2( &status_phrase[487], "Request Terminated");
pj_strset2( &status_phrase[488], "Not Acceptable Here");
- pj_strset2( &status_phrase[489], "Unknown Event");
+ pj_strset2( &status_phrase[489], "Bad Event");
pj_strset2( &status_phrase[490], "Request Updated");
pj_strset2( &status_phrase[491], "Request Pending");
pj_strset2( &status_phrase[493], "Undecipherable");
diff --git a/pjsip/src/pjsip/sip_ua_layer.c b/pjsip/src/pjsip/sip_ua_layer.c
index 71368a1b..6dbc9600 100644
--- a/pjsip/src/pjsip/sip_ua_layer.c
+++ b/pjsip/src/pjsip/sip_ua_layer.c
@@ -521,20 +521,32 @@ static pj_bool_t mod_ua_on_rx_request(pjsip_rx_data *rdata)
dlg = dlg->next;
}
- /* Dialog MUST be found! */
+ /* Dialog may not be found, e.g. in this case:
+ * - UAC sends SUBSCRIBE, then UAS sends NOTIFY before answering
+ * SUBSCRIBE request with 2xx.
+ *
+ * In this case, we can accept the request ONLY when the original
+ * dialog still has empty To tag.
+ */
if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) {
- /* Not found. Mulfunction UAC? */
- pj_mutex_unlock(mod_ua.mutex);
+ pjsip_dialog *first_dlg = dlg_set->dlg_list.next;
- PJ_LOG(5,(THIS_FILE,
- "Unable to find dialog for %s, answering with 481",
- pjsip_rx_data_get_info(rdata)));
+ if (first_dlg->remote.info->tag.slen != 0) {
+ /* Not found. Mulfunction UAC? */
+ pj_mutex_unlock(mod_ua.mutex);
- pjsip_endpt_respond_stateless(mod_ua.endpt, rdata,
- PJSIP_SC_CALL_TSX_DOES_NOT_EXIST,
- NULL, NULL, NULL);
- return PJ_TRUE;
+ PJ_LOG(5,(THIS_FILE,
+ "Unable to find dialog for %s, answering with 481",
+ pjsip_rx_data_get_info(rdata)));
+
+ pjsip_endpt_respond_stateless(mod_ua.endpt, rdata,
+ PJSIP_SC_CALL_TSX_DOES_NOT_EXIST,
+ NULL, NULL, NULL);
+ return PJ_TRUE;
+ }
+
+ dlg = first_dlg;
}
/* Mark the dialog id of the request. */
diff --git a/pjsip/src/pjsua/main.c b/pjsip/src/pjsua/main.c
index 37fde773..4a40d324 100644
--- a/pjsip/src/pjsua/main.c
+++ b/pjsip/src/pjsua/main.c
@@ -22,7 +22,8 @@
#define THIS_FILE "main.c"
-static pjsip_inv_session *inv_session;
+/* Current dialog */
+static struct pjsua_inv_data *inv_session;
/*
* Notify UI when invite state has changed.
@@ -35,12 +36,16 @@ void pjsua_ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e)
pjsua_inv_state_names[inv->state]));
if (inv->state == PJSIP_INV_STATE_DISCONNECTED) {
- if (inv == inv_session)
- inv_session = NULL;
+ if (inv == inv_session->inv) {
+ inv_session = inv_session->next;
+ if (inv_session == &pjsua.inv_list)
+ inv_session = pjsua.inv_list.next;
+ }
} else {
- inv_session = inv;
+ if (inv_session == &pjsua.inv_list || inv_session == NULL)
+ inv_session = inv->mod_data[pjsua.mod.id];
}
}
@@ -56,24 +61,71 @@ void pjsua_ui_regc_on_state_changed(int code)
}
+/*
+ * Print buddy list.
+ */
+static void print_buddy_list(void)
+{
+ unsigned i;
+
+ puts("Buddy list:");
+ //puts("-------------------------------------------------------------------------------");
+ if (pjsua.buddy_cnt == 0)
+ puts(" -none-");
+ else {
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+ const char *status;
+
+ if (pjsua.buddies[i].sub == NULL ||
+ pjsua.buddies[i].status.info_cnt==0)
+ {
+ status = " ? ";
+ }
+ else if (pjsua.buddies[i].status.info[0].basic_open)
+ status = " Online";
+ else
+ status = "Offline";
+
+ printf(" [%2d] <%s> %s\n",
+ i+1, status, pjsua.buddies[i].uri.ptr);
+ }
+ }
+ puts("");
+}
/*
* Show a bit of help.
*/
-static void ui_help(void)
+static void keystroke_help(void)
{
- puts("");
- puts("Console keys:");
- puts(" m Make a call/another call");
- puts(" d Dump application states");
- puts(" a Answer incoming call");
- puts(" h Hangup current call");
- puts(" q Quit");
- puts("");
+
+ printf(">>>>\nOnline status: %s\n",
+ (pjsua.online_status ? "Online" : "Invisible"));
+ print_buddy_list();
+
+ //puts("Commands:");
+ puts("+=============================================================================+");
+ puts("| Call Commands: | IM & Presence: | Misc: |");
+ puts("| | | |");
+ puts("| m Make new call | i Send IM | o Send OPTIONS |");
+ puts("| a Answer call | s Subscribe presence | d Dump status |");
+ puts("| h Hangup call | u Unsubscribe presence | d1 Dump detailed |");
+ puts("| ] Select next dialog | t Toggle Online status | |");
+ puts("| [ Select previous dialog | | |");
+ puts("+-----------------------------------------------------------------------------+");
+ puts("| q QUIT |");
+ puts("+=============================================================================+");
+ printf(">>> ");
+
+
fflush(stdout);
}
-static pj_bool_t input(const char *title, char *buf, pj_size_t len)
+
+/*
+ * Input simple string
+ */
+static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len)
{
char *p;
@@ -92,48 +144,131 @@ static pj_bool_t input(const char *title, char *buf, pj_size_t len)
return PJ_TRUE;
}
+
+#define NO_NB -2
+struct input_result
+{
+ int nb_result;
+ char *uri_result;
+};
+
+
+/*
+ * Input URL.
+ */
+static void ui_input_url(const char *title, char *buf, int len,
+ struct input_result *result)
+{
+ result->nb_result = NO_NB;
+ result->uri_result = NULL;
+
+ print_buddy_list();
+
+ printf("Choices:\n"
+ " 0 For current dialog.\n"
+ " -1 All %d buddies in buddy list\n"
+ " [1 -%2d] Select from buddy list\n"
+ " URL An URL\n"
+ " <Enter> Empty input (or 'q') to cancel\n"
+ , pjsua.buddy_cnt, pjsua.buddy_cnt);
+ printf("%s: ", title);
+
+ fflush(stdout);
+ fgets(buf, len, stdin);
+ len = strlen(buf);
+
+ /* Left trim */
+ while (isspace(*buf)) {
+ ++buf;
+ --len;
+ }
+
+ /* Remove trailing newlines */
+ while (len && (buf[len-1] == '\r' || buf[len-1] == '\n'))
+ buf[--len] = '\0';
+
+ if (len == 0 || buf[0]=='q')
+ return;
+
+ if (isdigit(*buf) || *buf=='-') {
+
+ int i;
+
+ if (*buf=='-')
+ i = 1;
+ else
+ i = 0;
+
+ for (; i<len; ++i) {
+ if (!isdigit(buf[i])) {
+ puts("Invalid input");
+ return;
+ }
+ }
+
+ result->nb_result = atoi(buf);
+
+ if (result->nb_result > 0 && result->nb_result <= (int)pjsua.buddy_cnt) {
+ --result->nb_result;
+ return;
+ }
+ if (result->nb_result == -1)
+ return;
+
+ puts("Invalid input");
+ result->nb_result = NO_NB;
+ return;
+
+ } else {
+ pj_status_t status;
+
+ if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) {
+ pjsua_perror("Invalid URL", status);
+ return;
+ }
+
+ result->uri_result = buf;
+ }
+}
+
static void ui_console_main(void)
{
+ char menuin[10];
char buf[128];
pjsip_inv_session *inv;
+ struct input_result result;
- //ui_help();
+ //keystroke_help();
for (;;) {
- ui_help();
- fgets(buf, sizeof(buf), stdin);
+ keystroke_help();
+ fgets(menuin, sizeof(menuin), stdin);
- switch (buf[0]) {
+ switch (menuin[0]) {
case 'm':
- if (inv_session != NULL) {
- puts("Can not make call while another one is in progress");
- fflush(stdout);
- continue;
- }
-
-#if 1
/* Make call! : */
- if (!input("Enter URL to call", buf, sizeof(buf)))
- continue;
- pjsua_invite(buf, &inv);
-
-#else
-
- pjsua_invite("sip:localhost:5061", &inv);
-#endif
+ if (pj_list_size(&pjsua.inv_list))
+ printf("(You have %d calls)\n", pj_list_size(&pjsua.inv_list));
+
+ ui_input_url("Make call", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+ if (result.nb_result == -1)
+ puts("You can't do that with make call!");
+ else
+ pjsua_invite(pjsua.buddies[result.nb_result].uri.ptr, &inv);
+ } else if (result.uri_result)
+ pjsua_invite(result.uri_result, &inv);
+
break;
- case 'd':
- pjsua_dump();
- break;
-
case 'a':
- if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS ||
- inv_session->state >= PJSIP_INV_STATE_CONNECTING)
+ if (inv_session == &pjsua.inv_list ||
+ inv_session->inv->role != PJSIP_ROLE_UAS ||
+ inv_session->inv->state >= PJSIP_INV_STATE_CONNECTING)
{
puts("No pending incoming call");
fflush(stdout);
@@ -143,16 +278,16 @@ static void ui_console_main(void)
pj_status_t status;
pjsip_tx_data *tdata;
- if (!input("Answer with code (100-699)", buf, sizeof(buf)))
+ if (!simple_input("Answer with code (100-699)", buf, sizeof(buf)))
continue;
if (atoi(buf) < 100)
continue;
- status = pjsip_inv_answer(inv_session, atoi(buf), NULL, NULL,
- &tdata);
+ status = pjsip_inv_answer(inv_session->inv, atoi(buf),
+ NULL, NULL, &tdata);
if (status == PJ_SUCCESS)
- status = pjsip_inv_send_msg(inv_session, tdata, NULL);
+ status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
if (status != PJ_SUCCESS)
pjsua_perror("Unable to create/send response", status);
@@ -160,9 +295,10 @@ static void ui_console_main(void)
break;
+
case 'h':
- if (inv_session == NULL) {
+ if (inv_session == &pjsua.inv_list) {
puts("No current call");
fflush(stdout);
continue;
@@ -171,22 +307,62 @@ static void ui_console_main(void)
pj_status_t status;
pjsip_tx_data *tdata;
- status = pjsip_inv_end_session(inv_session, PJSIP_SC_DECLINE,
- NULL, &tdata);
+ status = pjsip_inv_end_session(inv_session->inv,
+ PJSIP_SC_DECLINE, NULL, &tdata);
if (status != PJ_SUCCESS) {
pjsua_perror("Failed to create end session message", status);
continue;
}
- status = pjsip_inv_send_msg(inv_session, tdata, NULL);
+ status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL);
if (status != PJ_SUCCESS) {
pjsua_perror("Failed to send end session message", status);
continue;
}
}
+ break;
+
+ case ']':
+ inv_session = inv_session->next;
+ if (inv_session == &pjsua.inv_list)
+ inv_session = pjsua.inv_list.next;
+ break;
+
+ case '[':
+ inv_session = inv_session->prev;
+ if (inv_session == &pjsua.inv_list)
+ inv_session = pjsua.inv_list.prev;
+ break;
+
+ case 's':
+ case 'u':
+ ui_input_url("Subscribe presence of", buf, sizeof(buf), &result);
+ if (result.nb_result != NO_NB) {
+ if (result.nb_result == -1) {
+ unsigned i;
+ for (i=0; i<pjsua.buddy_cnt; ++i)
+ pjsua.buddies[i].monitor = (menuin[0]=='s');
+ } else {
+ pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s');
+ }
+
+ pjsua_pres_refresh();
+
+ } else if (result.uri_result) {
+ puts("Sorry, can only subscribe to buddy's presence, not arbitrary URL (for now)");
+ }
break;
+ case 't':
+ pjsua.online_status = !pjsua.online_status;
+ pjsua_pres_refresh();
+ break;
+
+ case 'd':
+ pjsua_dump();
+ break;
+
case 'q':
goto on_exit;
}
@@ -254,7 +430,7 @@ static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata)
static pjsip_module console_msg_logger =
{
NULL, NULL, /* prev, next. */
- { "mod-console-msg-logger", 22 }, /* Name. */
+ { "mod-pjsua-log", 13 }, /* Name. */
-1, /* Id */
PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */
NULL, /* User data. */
@@ -385,6 +561,11 @@ int main(int argc, char *argv[])
pj_thread_sleep(500);
+ /* No current call initially: */
+
+ inv_session = &pjsua.inv_list;
+
+
/* Start UI console main loop: */
ui_console_main();
diff --git a/pjsip/src/pjsua/pjsua.h b/pjsip/src/pjsua/pjsua.h
index ebb97a0d..202c2840 100644
--- a/pjsip/src/pjsua/pjsua.h
+++ b/pjsip/src/pjsua/pjsua.h
@@ -31,6 +31,9 @@
/* Include all PJSIP-UA headers */
#include <pjsip_ua.h>
+/* Include all PJSIP-SIMPLE headers */
+#include <pjsip_simple.h>
+
/* Include all PJLIB-UTIL headers. */
#include <pjlib-util.h>
@@ -61,6 +64,33 @@ struct pjsua_inv_data
};
+/**
+ * Buddy data.
+ */
+struct pjsua_buddy
+{
+ pj_str_t uri; /**< Buddy URI */
+ pj_bool_t monitor; /**< Should we monitor? */
+ pjsip_evsub *sub; /**< Buddy presence subscription */
+ pjsip_pres_status status; /**< Buddy presence status. */
+};
+
+typedef struct pjsua_buddy pjsua_buddy;
+
+
+/**
+ * Server presence subscription list head.
+ */
+struct pjsua_srv_pres
+{
+ PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres);
+ pjsip_evsub *sub;
+ char *remote;
+};
+
+typedef struct pjsua_srv_pres pjsua_srv_pres;
+
+
/* PJSUA application variables. */
struct pjsua
@@ -141,10 +171,13 @@ struct pjsua
struct pjsua_inv_data inv_list;
- /* Buddy list: */
+ /* SIMPLE and buddy status: */
+
+ pj_bool_t online_status; /**< Out online status. */
+ pjsua_srv_pres pres_srv_list; /**< Server subscription list. */
unsigned buddy_cnt;
- pj_str_t buddies[PJSUA_MAX_BUDDIES];
+ pjsua_buddy buddies[PJSUA_MAX_BUDDIES];
};
@@ -235,6 +268,12 @@ void pjsua_inv_on_new_session(pjsip_inv_session *inv, pjsip_event *e);
void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status);
+/**
+ * Terminate all calls.
+ */
+void pjsua_inv_shutdown(void);
+
+
/*****************************************************************************
* PJSUA Client Registration API (defined in pjsua_reg.c).
*/
@@ -253,6 +292,33 @@ pj_status_t pjsua_regc_init(void);
void pjsua_regc_update(pj_bool_t renew);
+
+
+/*****************************************************************************
+ * PJSUA Presence (pjsua_pres.c)
+ */
+
+/**
+ * Init presence.
+ */
+pj_status_t pjsua_pres_init();
+
+/**
+ * Refresh both presence client and server subscriptions.
+ */
+void pjsua_pres_refresh(void);
+
+/**
+ * Terminate all subscriptions
+ */
+void pjsua_pres_shutdown(void);
+
+/**
+ * Dump presence subscriptions.
+ */
+void pjsua_pres_dump(void);
+
+
/*****************************************************************************
* User Interface API.
*
diff --git a/pjsip/src/pjsua/pjsua_core.c b/pjsip/src/pjsua/pjsua_core.c
index f97c8f43..2d059d92 100644
--- a/pjsip/src/pjsua/pjsua_core.c
+++ b/pjsip/src/pjsua/pjsua_core.c
@@ -79,6 +79,11 @@ void pjsua_default(void)
/* Init invite session list: */
pj_list_init(&pjsua.inv_list);
+
+ /* Init server presence subscription list: */
+
+ pj_list_init(&pjsua.pres_srv_list);
+
}
@@ -391,14 +396,14 @@ on_error:
}
-static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg)
+static int PJ_THREAD_FUNC pjsua_poll(void *arg)
{
PJ_UNUSED_ARG(arg);
- while (!pjsua.quit_flag) {
+ do {
pj_time_val timeout = { 0, 10 };
pjsip_endpt_handle_events (pjsua.endpt, &timeout);
- }
+ } while (!pjsua.quit_flag);
return 0;
}
@@ -435,7 +440,7 @@ pj_status_t pjsua_init(void)
pjsua.pool = pj_pool_create(&pjsua.cp.factory, "pjsua", 4000, 4000, NULL);
- /* Init PJSIP and all the modules: */
+ /* Init PJSIP : */
status = init_stack();
if (status != PJ_SUCCESS) {
@@ -445,6 +450,20 @@ pj_status_t pjsua_init(void)
}
+ /* Init core SIMPLE module : */
+
+ pjsip_evsub_init_module(pjsua.endpt);
+
+ /* Init presence module: */
+
+ pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance());
+
+
+ /* Init pjsua presence handler: */
+
+ pjsua_pres_init();
+
+
/* Init media endpoint: */
status = pjmedia_endpt_create(&pjsua.cp.factory, &pjsua.med_endpt);
@@ -609,7 +628,7 @@ pj_status_t pjsua_start(void)
/* Create worker thread(s), if required: */
for (i=0; i<pjsua.thread_cnt; ++i) {
- status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_worker_thread,
+ status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll,
NULL, 0, 0, &pjsua.threads[i]);
if (status != PJ_SUCCESS) {
pjsua.quit_flag = 1;
@@ -635,11 +654,26 @@ pj_status_t pjsua_start(void)
}
-
+ PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION));
return PJ_SUCCESS;
}
+/* Sleep with polling */
+static void busy_sleep(unsigned msec)
+{
+ pj_time_val timeout, now;
+
+ pj_gettimeofday(&timeout);
+ timeout.msec += msec;
+ pj_time_val_normalize(&timeout);
+
+ do {
+ pjsua_poll(NULL);
+ pj_gettimeofday(&now);
+ } while (PJ_TIME_VAL_LT(now, timeout));
+}
+
/*
* Destroy pjsua.
*/
@@ -647,42 +681,43 @@ pj_status_t pjsua_destroy(void)
{
int i;
- /* Unregister, if required: */
- if (pjsua.regc) {
+ /* Signal threads to quit: */
+ pjsua.quit_flag = 1;
- pjsua_regc_update(0);
+ /* Wait worker threads to quit: */
+ for (i=0; i<pjsua.thread_cnt; ++i) {
+
+ if (pjsua.threads[i]) {
+ pj_thread_join(pjsua.threads[i]);
+ pj_thread_destroy(pjsua.threads[i]);
+ pjsua.threads[i] = NULL;
+ }
+ }
- /* Wait for some time to allow unregistration to complete: */
- pj_thread_sleep(500);
- }
+ /* Terminate all calls. */
+ pjsua_inv_shutdown();
- /* Signal threads to quit: */
+ /* Terminate all presence subscriptions. */
+ pjsua_pres_shutdown();
- pjsua.quit_flag = 1;
+ /* Unregister, if required: */
+ if (pjsua.regc) {
+ pjsua_regc_update(0);
+ }
+ /* Wait for some time to allow unregistration to complete: */
+ PJ_LOG(4,(THIS_FILE, "Shutting down..."));
+ busy_sleep(1000);
/* Shutdown pjmedia-codec: */
-
pjmedia_codec_deinit();
-
/* Destroy sound framework:
* (this should be done in pjmedia_shutdown())
*/
pj_snd_deinit();
- /* Wait worker threads to quit: */
-
- for (i=0; i<pjsua.thread_cnt; ++i) {
-
- if (pjsua.threads[i]) {
- pj_thread_join(pjsua.threads[i]);
- pj_thread_destroy(pjsua.threads[i]);
- pjsua.threads[i] = NULL;
- }
- }
-
/* Destroy endpoint. */
pjsip_endpt_destroy(pjsua.endpt);
diff --git a/pjsip/src/pjsua/pjsua_inv.c b/pjsip/src/pjsua/pjsua_inv.c
index 7c6dbf26..6f9607b5 100644
--- a/pjsip/src/pjsua/pjsua_inv.c
+++ b/pjsip/src/pjsua/pjsua_inv.c
@@ -80,6 +80,7 @@ pj_status_t pjsua_invite(const char *cstr_dest_uri,
inv_data = pj_pool_zalloc( dlg->pool, sizeof(struct pjsua_inv_data));
inv_data->inv = inv;
dlg->mod_data[pjsua.mod.id] = inv_data;
+ inv->mod_data[pjsua.mod.id] = inv_data;
/* Set dialog Route-Set: */
@@ -221,6 +222,7 @@ pj_bool_t pjsua_inv_on_incoming(pjsip_rx_data *rdata)
inv_data = pj_pool_zalloc(dlg->pool, sizeof(struct pjsua_inv_data));
inv_data->inv = inv;
dlg->mod_data[pjsua.mod.id] = inv_data;
+ inv->mod_data[pjsua.mod.id] = inv_data;
pj_list_push_back(&pjsua.inv_list, inv_data);
@@ -345,3 +347,25 @@ void pjsua_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
PJ_LOG(3,(THIS_FILE,"Media has been started successfully"));
}
}
+
+
+/*
+ * Terminate all calls.
+ */
+void pjsua_inv_shutdown()
+{
+ struct pjsua_inv_data *inv_data, *next;
+
+ inv_data = pjsua.inv_list.next;
+ while (inv_data != &pjsua.inv_list) {
+ pjsip_tx_data *tdata;
+
+ next = inv_data->next;
+
+ if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0)
+ pjsip_inv_send_msg(inv_data->inv, tdata, NULL);
+
+ inv_data = next;
+ }
+}
+
diff --git a/pjsip/src/pjsua/pjsua_opt.c b/pjsip/src/pjsua/pjsua_opt.c
index 61e5adba..5ec1a56a 100644
--- a/pjsip/src/pjsua/pjsua_opt.c
+++ b/pjsip/src/pjsua/pjsua_opt.c
@@ -403,7 +403,7 @@ pj_status_t pjsua_parse_args(int argc, char *argv[])
printf("Error: too many buddies in buddy list.\n");
return -1;
}
- pjsua.buddies[pjsua.buddy_cnt++] = pj_str(optarg);
+ pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(optarg);
break;
}
}
@@ -510,6 +510,7 @@ void pjsua_dump(void)
pjsip_endpt_dump(pjsua.endpt, 1);
pjmedia_endpt_dump(pjsua.med_endpt);
+ pjsip_tsx_layer_dump();
pjsip_ua_dump();
@@ -536,6 +537,9 @@ void pjsua_dump(void)
}
}
+ /* Dump presence status */
+ pjsua_pres_dump();
+
pj_log_set_decor(old_decor);
PJ_LOG(3,(THIS_FILE, "Dump complete"));
}
@@ -547,8 +551,9 @@ void pjsua_dump(void)
pj_status_t pjsua_load_settings(const char *filename)
{
int argc = 3;
- char *argv[] = { "pjsua", "--config-file", (char*)filename, NULL};
+ char *argv[4] = { "pjsua", "--config-file", NULL, NULL};
+ argv[3] = (char*)filename;
return pjsua_parse_args(argc, argv);
}
@@ -654,8 +659,8 @@ pj_status_t pjsua_save_settings(const char *filename)
/* Add buddies. */
for (i=0; i<pjsua.buddy_cnt; ++i) {
pj_ansi_sprintf(line, "--add-buddy %.*s\n",
- (int)pjsua.buddies[i].slen,
- pjsua.buddies[i].ptr);
+ (int)pjsua.buddies[i].uri.slen,
+ pjsua.buddies[i].uri.ptr);
pj_strcat2(&cfg, line);
}
diff --git a/pjsip/src/pjsua/pjsua_pres.c b/pjsip/src/pjsua/pjsua_pres.c
new file mode 100644
index 00000000..db009eed
--- /dev/null
+++ b/pjsip/src/pjsua/pjsua_pres.c
@@ -0,0 +1,471 @@
+/* $Id$ */
+/*
+ * Copyright (C) 2003-2006 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "pjsua.h"
+
+/*
+ * pjsua_pres.c
+ *
+ * Presence related stuffs.
+ */
+
+#define THIS_FILE "pjsua_pres.c"
+
+
+
+/* **************************************************************************
+ * THE FOLLOWING PART HANDLES SERVER SUBSCRIPTION
+ * **************************************************************************
+ */
+
+/* Proto */
+static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata);
+
+/* The module instance. */
+static pjsip_module mod_pjsua_pres =
+{
+ NULL, NULL, /* prev, next. */
+ { "mod-pjsua-pres", 14 }, /* Name. */
+ -1, /* Id */
+ PJSIP_MOD_PRIORITY_APPLICATION, /* Priority */
+ NULL, /* User data. */
+ NULL, /* load() */
+ NULL, /* start() */
+ NULL, /* stop() */
+ NULL, /* unload() */
+ &pres_on_rx_request, /* on_rx_request() */
+ NULL, /* on_rx_response() */
+ NULL, /* on_tx_request. */
+ NULL, /* on_tx_response() */
+ NULL, /* on_tsx_state() */
+
+};
+
+
+/* Callback called when *server* subscription state has changed. */
+static void pres_evsub_on_srv_state( pjsip_evsub *sub, pjsip_event *event)
+{
+ pjsua_srv_pres *uapres = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+
+ PJ_UNUSED_ARG(event);
+
+ if (uapres) {
+ PJ_LOG(3,(THIS_FILE, "Server subscription to %s is %s",
+ uapres->remote, pjsip_evsub_get_state_name(sub)));
+
+ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
+ pj_list_erase(uapres);
+ }
+ }
+}
+
+/* This is called when request is received.
+ * We need to check for incoming SUBSCRIBE request.
+ */
+static pj_bool_t pres_on_rx_request(pjsip_rx_data *rdata)
+{
+ pjsip_method *req_method = &rdata->msg_info.msg->line.req.method;
+ pjsua_srv_pres *uapres;
+ pjsip_evsub *sub;
+ pjsip_evsub_user pres_cb;
+ pjsip_tx_data *tdata;
+ pjsip_pres_status pres_status;
+ pjsip_dialog *dlg;
+ pj_status_t status;
+
+ if (pjsip_method_cmp(req_method, &pjsip_subscribe_method) != 0)
+ return PJ_FALSE;
+
+ /* Incoming SUBSCRIBE: */
+
+ /* Create UAS dialog: */
+ status = pjsip_dlg_create_uas( pjsip_ua_instance(), rdata,
+ &pjsua.contact_uri, &dlg);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror("Unable to create UAS dialog for subscription", status);
+ return PJ_FALSE;
+ }
+
+ /* Init callback: */
+ pj_memset(&pres_cb, 0, sizeof(pres_cb));
+ pres_cb.on_evsub_state = &pres_evsub_on_srv_state;
+
+ /* Create server presence subscription: */
+ status = pjsip_pres_create_uas( dlg, &pres_cb, rdata, &sub);
+ if (status != PJ_SUCCESS) {
+ PJ_TODO(DESTROY_DIALOG);
+ pjsua_perror("Unable to create server subscription", status);
+ return PJ_FALSE;
+ }
+
+ /* Attach our data to the subscription: */
+ uapres = pj_pool_alloc(dlg->pool, sizeof(pjsua_srv_pres));
+ uapres->sub = sub;
+ uapres->remote = pj_pool_alloc(dlg->pool, PJSIP_MAX_URL_SIZE);
+ status = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, dlg->remote.info->uri,
+ uapres->remote, PJSIP_MAX_URL_SIZE);
+ if (status < 1)
+ pj_ansi_strcpy(uapres->remote, "<-- url is too long-->");
+ else
+ uapres->remote[status] = '\0';
+
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, uapres);
+
+ /* Add server subscription to the list: */
+ pj_list_push_back(&pjsua.pres_srv_list, uapres);
+
+
+ /* Create and send 200 (OK) to the SUBSCRIBE request: */
+ status = pjsip_pres_accept(sub, rdata, 200, NULL);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror("Unable to accept presence subscription", status);
+ pj_list_erase(uapres);
+ return PJ_FALSE;
+ }
+
+
+ /* Set our online status: */
+ pj_memset(&pres_status, 0, sizeof(pres_status));
+ pres_status.info_cnt = 1;
+ pres_status.info[0].basic_open = pjsua.online_status;
+ //Both pjsua.local_uri and pjsua.contact_uri are enclosed in "<" and ">"
+ //causing XML parsing to fail.
+ //pres_status.info[0].contact = pjsua.local_uri;
+
+ pjsip_pres_set_status(sub, &pres_status);
+
+ /* Create and send the first NOTIFY to active subscription: */
+ status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_ACTIVE, NULL,
+ NULL, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_pres_send_request( sub, tdata);
+
+ if (status != PJ_SUCCESS) {
+ pjsua_perror("Unable to create/send NOTIFY", status);
+ pj_list_erase(uapres);
+ return PJ_FALSE;
+ }
+
+
+ /* Done: */
+
+ return PJ_TRUE;
+}
+
+
+/* Refresh subscription (e.g. when our online status has changed) */
+static void refresh_server_subscription()
+{
+ pjsua_srv_pres *uapres;
+
+ uapres = pjsua.pres_srv_list.next;
+
+ while (uapres != &pjsua.pres_srv_list) {
+
+ pjsip_pres_status pres_status;
+ pjsip_tx_data *tdata;
+
+ pjsip_pres_get_status(uapres->sub, &pres_status);
+ if (pres_status.info[0].basic_open != pjsua.online_status) {
+ pres_status.info[0].basic_open = pjsua.online_status;
+ pjsip_pres_set_status(uapres->sub, &pres_status);
+
+ if (pjsua.quit_flag) {
+ pj_str_t reason = { "noresource", 10 };
+ if (pjsip_pres_notify(uapres->sub,
+ PJSIP_EVSUB_STATE_TERMINATED, NULL,
+ &reason, &tdata)==PJ_SUCCESS)
+ {
+ pjsip_pres_send_request(uapres->sub, tdata);
+ }
+ } else {
+ if (pjsip_pres_current_notify(uapres->sub, &tdata)==PJ_SUCCESS)
+ pjsip_pres_send_request(uapres->sub, tdata);
+ }
+ }
+
+ uapres = uapres->next;
+ }
+}
+
+
+
+/* **************************************************************************
+ * THE FOLLOWING PART HANDLES CLIENT SUBSCRIPTION
+ * **************************************************************************
+ */
+
+/* Callback called when *client* subscription state has changed. */
+static void pjsua_evsub_on_state( pjsip_evsub *sub, pjsip_event *event)
+{
+ pjsua_buddy *buddy;
+
+ PJ_UNUSED_ARG(event);
+
+ buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+ if (buddy) {
+ PJ_LOG(3,(THIS_FILE,
+ "Presence subscription to %s is %s",
+ buddy->uri.ptr,
+ pjsip_evsub_get_state_name(sub)));
+
+ if (pjsip_evsub_get_state(sub) == PJSIP_EVSUB_STATE_TERMINATED) {
+ buddy->sub = NULL;
+ buddy->status.info_cnt = 0;
+ pjsip_evsub_set_mod_data(sub, pjsua.mod.id, NULL);
+ }
+ }
+}
+
+/* Callback called when we receive NOTIFY */
+static void pjsua_evsub_on_rx_notify(pjsip_evsub *sub,
+ pjsip_rx_data *rdata,
+ int *p_st_code,
+ pj_str_t **p_st_text,
+ pjsip_hdr *res_hdr,
+ pjsip_msg_body **p_body)
+{
+ pjsua_buddy *buddy;
+
+ buddy = pjsip_evsub_get_mod_data(sub, pjsua.mod.id);
+ if (buddy) {
+ /* Update our info. */
+ pjsip_pres_get_status(sub, &buddy->status);
+
+ if (buddy->status.info_cnt) {
+ PJ_LOG(3,(THIS_FILE, "%s is %s",
+ buddy->uri.ptr,
+ (buddy->status.info[0].basic_open?"online":"offline")));
+ } else {
+ PJ_LOG(3,(THIS_FILE, "No presence info for %s",
+ buddy->uri.ptr));
+ }
+ }
+
+ /* The default is to send 200 response to NOTIFY.
+ * Just leave it there..
+ */
+ PJ_UNUSED_ARG(rdata);
+ PJ_UNUSED_ARG(p_st_code);
+ PJ_UNUSED_ARG(p_st_text);
+ PJ_UNUSED_ARG(res_hdr);
+ PJ_UNUSED_ARG(p_body);
+}
+
+
+/* Event subscription callback. */
+static pjsip_evsub_user pres_callback =
+{
+ &pjsua_evsub_on_state,
+
+ NULL, /* on_tsx_state: don't care about transaction state. */
+
+ NULL, /* on_rx_refresh: don't care about SUBSCRIBE refresh, unless
+ * we want to authenticate
+ */
+
+ &pjsua_evsub_on_rx_notify,
+
+ NULL, /* on_client_refresh: Use default behaviour, which is to
+ * refresh client subscription. */
+
+ NULL, /* on_server_timeout: Use default behaviour, which is to send
+ * NOTIFY to terminate.
+ */
+};
+
+
+/* It does what it says.. */
+static void subscribe_buddy_presence(unsigned index)
+{
+ pjsip_dialog *dlg;
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ status = pjsip_dlg_create_uac( pjsip_ua_instance(),
+ &pjsua.local_uri,
+ &pjsua.contact_uri,
+ &pjsua.buddies[index].uri,
+ NULL, &dlg);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror("Unable to create dialog", status);
+ return;
+ }
+
+ status = pjsip_pres_create_uac( dlg, &pres_callback,
+ &pjsua.buddies[index].sub);
+ if (status != PJ_SUCCESS) {
+ pjsua.buddies[index].sub = NULL;
+ pjsua_perror("Unable to create presence client", status);
+ return;
+ }
+
+ pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id,
+ &pjsua.buddies[index]);
+
+ status = pjsip_pres_initiate(pjsua.buddies[index].sub, 60, &tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua.buddies[index].sub = NULL;
+ pjsua_perror("Unable to create initial SUBSCRIBE", status);
+ return;
+ }
+
+ status = pjsip_pres_send_request(pjsua.buddies[index].sub, tdata);
+ if (status != PJ_SUCCESS) {
+ pjsua.buddies[index].sub = NULL;
+ pjsua_perror("Unable to send initial SUBSCRIBE", status);
+ return;
+ }
+
+ PJ_TODO(DESTROY_DIALOG_ON_ERROR);
+}
+
+
+/* It does what it says... */
+static void unsubscribe_buddy_presence(unsigned index)
+{
+ pjsip_tx_data *tdata;
+ pj_status_t status;
+
+ if (pjsua.buddies[index].sub == NULL)
+ return;
+
+ if (pjsip_evsub_get_state(pjsua.buddies[index].sub) ==
+ PJSIP_EVSUB_STATE_TERMINATED)
+ {
+ pjsua.buddies[index].sub = NULL;
+ return;
+ }
+
+ status = pjsip_pres_initiate( pjsua.buddies[index].sub, 0, &tdata);
+ if (status == PJ_SUCCESS)
+ status = pjsip_pres_send_request( pjsua.buddies[index].sub, tdata );
+
+ if (status == PJ_SUCCESS) {
+
+ //pjsip_evsub_set_mod_data(pjsua.buddies[index].sub, pjsua.mod.id,
+ // NULL);
+ //pjsua.buddies[index].sub = NULL;
+
+ } else {
+ pjsua_perror("Unable to unsubscribe presence", status);
+ }
+}
+
+
+/* It does what it says.. */
+static void refresh_client_subscription(void)
+{
+ unsigned i;
+
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+
+ if (pjsua.buddies[i].monitor && !pjsua.buddies[i].sub) {
+ subscribe_buddy_presence(i);
+
+ } else if (!pjsua.buddies[i].monitor && pjsua.buddies[i].sub) {
+ unsubscribe_buddy_presence(i);
+
+ }
+ }
+}
+
+
+/*
+ * Init presence
+ */
+pj_status_t pjsua_pres_init()
+{
+ pj_status_t status;
+
+ status = pjsip_endpt_register_module( pjsua.endpt, &mod_pjsua_pres);
+ if (status != PJ_SUCCESS) {
+ pjsua_perror("Unable to register pjsua presence module", status);
+ }
+
+ return status;
+}
+
+/*
+ * Refresh presence
+ */
+void pjsua_pres_refresh(void)
+{
+ refresh_client_subscription();
+ refresh_server_subscription();
+}
+
+
+/*
+ * Shutdown presence.
+ */
+void pjsua_pres_shutdown(void)
+{
+ unsigned i;
+
+ pjsua.online_status = 0;
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+ pjsua.buddies[i].monitor = 0;
+ }
+ pjsua_pres_refresh();
+}
+
+/*
+ * Dump presence status.
+ */
+void pjsua_pres_dump(void)
+{
+ unsigned i;
+
+ PJ_LOG(3,(THIS_FILE, "Dumping pjsua server subscriptions:"));
+ if (pj_list_empty(&pjsua.pres_srv_list)) {
+ PJ_LOG(3,(THIS_FILE, " - none - "));
+ } else {
+ struct pjsua_srv_pres *uapres;
+
+ uapres = pjsua.pres_srv_list.next;
+ while (uapres != &pjsua.pres_srv_list) {
+
+ PJ_LOG(3,(THIS_FILE, " %10s %s",
+ pjsip_evsub_get_state_name(uapres->sub),
+ uapres->remote));
+
+ uapres = uapres->next;
+ }
+ }
+
+ PJ_LOG(3,(THIS_FILE, "Dumping pjsua client subscriptions:"));
+ if (pjsua.buddy_cnt == 0) {
+ PJ_LOG(3,(THIS_FILE, " - no buddy list - "));
+ } else {
+ for (i=0; i<pjsua.buddy_cnt; ++i) {
+
+ if (pjsua.buddies[i].sub) {
+ PJ_LOG(3,(THIS_FILE, " %10s %s",
+ pjsip_evsub_get_state_name(pjsua.buddies[i].sub),
+ pjsua.buddies[i].uri.ptr));
+ } else {
+ PJ_LOG(3,(THIS_FILE, " %10s %s",
+ "(null)",
+ pjsua.buddies[i].uri.ptr));
+ }
+ }
+ }
+}
+