- Timestamp:
- Feb 19, 2006 1:38:06 AM (19 years ago)
- Location:
- pjproject/trunk/pjsip
- Files:
-
- 3 added
- 2 deleted
- 24 edited
- 4 moved
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip/build/pjsip.dsw
r184 r197 94 94 ############################################################################### 95 95 96 Project: "pjsip_simple"=.\pjsip_simple.dsp - Package Owner=<4> 97 98 Package=<5> 99 {{{ 100 }}} 101 102 Package=<4> 103 {{{ 104 }}} 105 106 ############################################################################### 107 96 108 Project: "pjsip_ua"=.\pjsip_ua.dsp - Package Owner=<4> 97 109 … … 135 147 Project_Dep_Name pjlib_util 136 148 End Project Dependency 149 Begin Project Dependency 150 Project_Dep_Name pjsip_simple 151 End Project Dependency 137 152 }}} 138 153 -
pjproject/trunk/pjsip/build/pjsip_simple.dsp
r65 r197 33 33 # PROP BASE Use_MFC 0 34 34 # PROP BASE Use_Debug_Libraries 0 35 # PROP BASE Output_Dir "./output/pjsip _simple_Win32_Release"36 # PROP BASE Intermediate_Dir "./output/pjsip _simple_Win32_Release"35 # PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-release" 36 # PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-release" 37 37 # PROP BASE Target_Dir "" 38 38 # PROP Use_MFC 0 39 39 # PROP Use_Debug_Libraries 0 40 # PROP Output_Dir "./output/pjsip _simple_Win32_Release"41 # PROP Intermediate_Dir "./output/pjsip _simple_Win32_Release"40 # PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-release" 41 # PROP Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-release" 42 42 # PROP Target_Dir "" 43 43 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c 44 # ADD CPP /nologo /MD /W3 /GX /O2 /I "../ src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c44 # 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 45 45 # ADD BASE RSC /l 0x409 /d "NDEBUG" 46 46 # ADD RSC /l 0x409 /d "NDEBUG" … … 50 50 LIB32=link.exe -lib 51 51 # ADD BASE LIB32 /nologo 52 # ADD LIB32 /nologo /out:"../lib/pjsip _simple_vc6.lib"52 # ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-release.lib" 53 53 54 54 !ELSEIF "$(CFG)" == "pjsip_simple - Win32 Debug" … … 56 56 # PROP BASE Use_MFC 0 57 57 # PROP BASE Use_Debug_Libraries 1 58 # PROP BASE Output_Dir "./output/pjsip _simple_Win32_Debug"59 # PROP BASE Intermediate_Dir "./output/pjsip _simple_Win32_Debug"58 # PROP BASE Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug" 59 # PROP BASE Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-debug" 60 60 # PROP BASE Target_Dir "" 61 61 # PROP Use_MFC 0 62 62 # PROP Use_Debug_Libraries 1 63 # PROP Output_Dir "./output/pjsip _simple_Win32_Debug"64 # PROP Intermediate_Dir "./output/pjsip _simple_Win32_Debug"63 # PROP Output_Dir "./output/pjsip-simple-i386-win32-vc6-debug" 64 # PROP Intermediate_Dir "./output/pjsip-simple-i386-win32-vc6-debug" 65 65 # PROP Target_Dir "" 66 66 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 67 # 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 /c67 # 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 68 68 # ADD BASE RSC /l 0x409 /d "_DEBUG" 69 69 # ADD RSC /l 0x409 /d "_DEBUG" … … 73 73 LIB32=link.exe -lib 74 74 # ADD BASE LIB32 /nologo 75 # ADD LIB32 /nologo /out:"../lib/pjsip _simple_vc6d.lib"75 # ADD LIB32 /nologo /out:"../lib/pjsip-simple-i386-win32-vc6-debug.lib" 76 76 77 77 !ENDIF … … 86 86 # Begin Source File 87 87 88 SOURCE= ..\src\pjsip_simple\event_notify.c88 SOURCE="..\src\pjsip-simple\evsub.c" 89 89 # End Source File 90 90 # Begin Source File 91 91 92 SOURCE= ..\src\pjsip_simple\event_notify_msg.c92 SOURCE="..\src\pjsip-simple\evsub_msg.c" 93 93 # End Source File 94 94 # Begin Source File 95 95 96 SOURCE= ..\src\pjsip_simple\messaging.c96 SOURCE="..\src\pjsip-simple\pidf.c" 97 97 # End Source File 98 98 # Begin Source File 99 99 100 SOURCE= ..\src\pjsip_simple\pidf.c100 SOURCE="..\src\pjsip-simple\presence.c" 101 101 # End Source File 102 102 # Begin Source File 103 103 104 SOURCE=..\src\pjsip_simple\presence.c 105 # End Source File 106 # Begin Source File 107 108 SOURCE=..\src\pjsip_simple\xpidf.c 104 SOURCE="..\src\pjsip-simple\xpidf.c" 109 105 # End Source File 110 106 # End Group … … 114 110 # Begin Source File 115 111 116 SOURCE= ..\src\pjsip_simple\event_notify.h112 SOURCE="..\include\pjsip-simple\errno.h" 117 113 # End Source File 118 114 # Begin Source File 119 115 120 SOURCE= ..\src\pjsip_simple\event_notify_msg.h116 SOURCE="..\include\pjsip-simple\evsub.h" 121 117 # End Source File 122 118 # Begin Source File 123 119 124 SOURCE= ..\src\pjsip_simple\messaging.h120 SOURCE="..\include\pjsip-simple\evsub_msg.h" 125 121 # End Source File 126 122 # Begin Source File 127 123 128 SOURCE= ..\src\pjsip_simple\pidf.h124 SOURCE="..\include\pjsip-simple\pidf.h" 129 125 # End Source File 130 126 # Begin Source File 131 127 132 SOURCE=..\ src\pjsip_simple.h128 SOURCE=..\include\pjsip_simple.h 133 129 # End Source File 134 130 # Begin Source File 135 131 136 SOURCE= ..\src\pjsip_simple\presence.h132 SOURCE="..\include\pjsip-simple\presence.h" 137 133 # End Source File 138 134 # Begin Source File 139 135 140 SOURCE=..\src\pjsip_simple\xpidf.h 136 SOURCE="..\include\pjsip-simple\types.h" 137 # End Source File 138 # Begin Source File 139 140 SOURCE="..\include\pjsip-simple\xpidf.h" 141 141 # End Source File 142 142 # End Group -
pjproject/trunk/pjsip/build/pjsip_ua.dsp
r141 r197 33 33 # PROP BASE Use_MFC 0 34 34 # PROP BASE Use_Debug_Libraries 0 35 # PROP BASE Output_Dir ".\output\pjsip _ua_vc6_Release"36 # PROP BASE Intermediate_Dir ".\output\pjsip _ua_vc6_Release"35 # PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release" 36 # PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-release" 37 37 # PROP BASE Target_Dir "" 38 38 # PROP Use_MFC 0 39 39 # PROP Use_Debug_Libraries 0 40 # PROP Output_Dir ".\output\pjsip _ua_vc6_Release"41 # PROP Intermediate_Dir ".\output\pjsip _ua_vc6_Release"40 # PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-release" 41 # PROP Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-release" 42 42 # PROP Target_Dir "" 43 43 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c … … 51 51 LIB32=link.exe -lib 52 52 # ADD BASE LIB32 /nologo 53 # ADD LIB32 /nologo /out:"../lib/pjsip _ua_vc6s.lib"53 # ADD LIB32 /nologo /out:"../lib/pjsip-ua-i386-win32-vc6-release.lib" 54 54 55 55 !ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug" … … 57 57 # PROP BASE Use_MFC 0 58 58 # PROP BASE Use_Debug_Libraries 1 59 # PROP BASE Output_Dir ".\output\pjsip _ua_vc6_Debug"60 # PROP BASE Intermediate_Dir ".\output\pjsip _ua_vc6_Debug"59 # PROP BASE Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" 60 # PROP BASE Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" 61 61 # PROP BASE Target_Dir "" 62 62 # PROP Use_MFC 0 63 63 # PROP Use_Debug_Libraries 1 64 # PROP Output_Dir ".\output\pjsip _ua_vc6_Debug"65 # PROP Intermediate_Dir ".\output\pjsip _ua_vc6_Debug"64 # PROP Output_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" 65 # PROP Intermediate_Dir ".\output\pjsip-ua-i386-win32-vc6-debug" 66 66 # PROP Target_Dir "" 67 67 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c … … 75 75 LIB32=link.exe -lib 76 76 # ADD BASE LIB32 /nologo 77 # ADD LIB32 /nologo /out:"../lib/pjsip _ua_vc6sd.lib"77 # ADD LIB32 /nologo /out:"../lib/pjsip-ua-i386-win32-vc6-debug.lib" 78 78 79 79 !ENDIF … … 93 93 94 94 SOURCE="..\src\pjsip-ua\sip_reg.c" 95 96 !IF "$(CFG)" == "pjsip_ua - Win32 Release"97 98 !ELSEIF "$(CFG)" == "pjsip_ua - Win32 Debug"99 100 !ENDIF101 102 95 # End Source File 103 96 # End Group -
pjproject/trunk/pjsip/build/pjsua.dsp
r184 r197 33 33 # PROP BASE Use_MFC 0 34 34 # PROP BASE Use_Debug_Libraries 0 35 # PROP BASE Output_Dir ".\output\pjsua _vc6_Release"36 # PROP BASE Intermediate_Dir ".\output\pjsua _vc6_Release"35 # PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-release" 36 # PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release" 37 37 # PROP BASE Target_Dir "" 38 38 # PROP Use_MFC 0 39 39 # PROP Use_Debug_Libraries 0 40 # PROP Output_Dir ".\output\pjsua _vc6_Release"41 # PROP Intermediate_Dir ".\output\pjsua _vc6_Release"40 # PROP Output_Dir ".\output\pjsua-i386-win32-vc6-release" 41 # PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-release" 42 42 # PROP Ignore_Export_Lib 0 43 43 # PROP Target_Dir "" … … 59 59 # PROP BASE Use_MFC 0 60 60 # PROP BASE Use_Debug_Libraries 1 61 # PROP BASE Output_Dir ".\output\pjsua _vc6_Debug"62 # PROP BASE Intermediate_Dir ".\output\pjsua _vc6_Debug"61 # PROP BASE Output_Dir ".\output\pjsua-i386-win32-vc6-debug" 62 # PROP BASE Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug" 63 63 # PROP BASE Target_Dir "" 64 64 # PROP Use_MFC 0 65 65 # PROP Use_Debug_Libraries 1 66 # PROP Output_Dir ".\output\pjsua _vc6_Debug"67 # PROP Intermediate_Dir ".\output\pjsua _vc6_Debug"66 # PROP Output_Dir ".\output\pjsua-i386-win32-vc6-debug" 67 # PROP Intermediate_Dir ".\output\pjsua-i386-win32-vc6-debug" 68 68 # PROP Ignore_Export_Lib 0 69 69 # PROP Target_Dir "" … … 111 111 # Begin Source File 112 112 113 SOURCE=..\src\pjsua\pjsua_pres.c 114 # End Source File 115 # Begin Source File 116 113 117 SOURCE=..\src\pjsua\pjsua_reg.c 114 118 # End Source File -
pjproject/trunk/pjsip/include/pjsip-simple/evsub.h
- Property svn:keywords set to id
r187 r197 15 15 * You should have received a copy of the GNU General Public License 16 16 * along with this program; if not, write to the Free Software 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 #ifndef __PJSIP_SIMPLE_EV ENT_NOTIFY_H__20 #define __PJSIP_SIMPLE_EV ENT_NOTIFY_H__17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 */ 19 #ifndef __PJSIP_SIMPLE_EVSUB_H__ 20 #define __PJSIP_SIMPLE_EVSUB_H__ 21 21 22 22 /** … … 25 25 */ 26 26 27 #include <pjsip/sip_types.h> 28 #include <pjsip/sip_auth.h> 29 #include <pjsip_simple/event_notify_msg.h> 30 #include <pj/timer.h> 27 #include <pjsip-simple/types.h> 28 31 29 32 30 /** … … 45 43 PJ_BEGIN_DECL 46 44 47 typedef struct pjsip_event_sub_cb pjsip_event_sub_cb; 48 typedef struct pjsip_event_sub pjsip_event_sub; 49 50 /** 51 * This enumeration describes subscription state as described in the RFC 3265. 52 * The standard specifies that extensions may define additional states. In the 53 * case where the state is not known, the subscription state will be set to 54 * PJSIP_EVENT_SUB_STATE_UNKNOWN, and the token will be kept in state_str 55 * member of the susbcription structure. 56 */ 57 typedef enum pjsip_event_sub_state 45 46 /** 47 * Opaque type for event subscription session. 48 */ 49 typedef struct pjsip_evsub pjsip_evsub; 50 51 52 /** 53 * This enumeration describes basic subscription state as described in the 54 * RFC 3265. The standard specifies that extensions may define additional 55 * states. In the case where the state is not known, the subscription state 56 * will be set to PJSIP_EVSUB_STATE_UNKNOWN, and the token will be kept 57 * in state_str member of the susbcription structure. 58 */ 59 enum pjsip_evsub_state 58 60 { 59 /** State is NULL. */ 60 PJSIP_EVENT_SUB_STATE_NULL, 61 62 /** Subscription is active. */ 63 PJSIP_EVENT_SUB_STATE_ACTIVE, 64 65 /** Subscription is pending. */ 66 PJSIP_EVENT_SUB_STATE_PENDING, 67 68 /** Subscription is terminated. */ 69 PJSIP_EVENT_SUB_STATE_TERMINATED, 70 71 /** Subscription state can not be determined. Application can query 72 * the state information in state_str member. 73 */ 74 PJSIP_EVENT_SUB_STATE_UNKNOWN, 75 76 } pjsip_event_sub_state; 77 78 /** 79 * This structure describes notification to be called when incoming SUBSCRIBE 80 * request is received. The module will call the callback registered by package 81 * that matches the event description in the incoming SUBSCRIBE. 82 */ 83 typedef struct pjsip_event_sub_pkg_cb 61 PJSIP_EVSUB_STATE_NULL, /**< State is NULL. */ 62 PJSIP_EVSUB_STATE_SENT, /**< Client has sent SUBSCRIBE request. */ 63 PJSIP_EVSUB_STATE_ACCEPTED, /**< 2xx response to SUBSCRIBE has been 64 sent/received. */ 65 PJSIP_EVSUB_STATE_PENDING, /**< Subscription is pending. */ 66 PJSIP_EVSUB_STATE_ACTIVE, /**< Subscription is active. */ 67 PJSIP_EVSUB_STATE_TERMINATED,/**< Subscription is terminated. */ 68 PJSIP_EVSUB_STATE_UNKNOWN, /**< Subscription state can not be determined. 69 Application can query the state by 70 calling #pjsip_evsub_get_state_name().*/ 71 }; 72 73 /** 74 * @see pjsip_evsub_state 75 */ 76 typedef enum pjsip_evsub_state pjsip_evsub_state; 77 78 79 80 /** 81 * This structure describes callback that is registered by application or 82 * package to receive notifications about subscription events. 83 */ 84 struct pjsip_evsub_user 84 85 { 85 86 /** 86 * This callback is called to first enquery the package whether it wants 87 * to accept incoming SUBSCRIBE request. If it does, then on_subscribe 88 * will be called. 89 * 90 * @param rdata The incoming request. 91 * @param status The status code to be returned back to subscriber. 92 */ 93 void (*on_query_subscribe)(pjsip_rx_data *rdata, int *status); 94 95 /** 96 * This callback is called when the module receives incoming SUBSCRIBE 97 * request. 87 * This callback is called when subscription state has changed. 88 * Application MUST be prepared to receive NULL event and events with 89 * type other than PJSIP_EVENT_TSX_STATE 90 * 91 * This callback is OPTIONAL. 98 92 * 99 93 * @param sub The subscription instance. 100 * @param rdata The received buffer. 101 * @param cb Callback to be registered to the subscription instance. 102 * @param expires The expiration to be set. 103 */ 104 void (*on_subscribe)(pjsip_event_sub *sub, pjsip_rx_data *rdata, 105 pjsip_event_sub_cb **cb, int *expires); 106 107 } pjsip_event_sub_pkg_cb; 108 109 /** 110 * This structure describes callback that is registered by application or 111 * package to receive notifications about a subscription. 112 */ 113 struct pjsip_event_sub_cb 114 { 115 /** 116 * This callback is used by both subscriber and notifier. It is called 117 * when the subscription has been terminated. 94 * @param event The event that has caused the state to change, 95 * which may be NULL or may have type other than 96 * PJSIP_EVENT_TSX_STATE. 97 */ 98 void (*on_evsub_state)( pjsip_evsub *sub, pjsip_event *event); 99 100 /** 101 * This callback is called when transaction state has changed. 118 102 * 119 103 * @param sub The subscription instance. 120 * @param reason The termination reason. 121 */ 122 void (*on_sub_terminated)(pjsip_event_sub *sub, const pj_str_t *reason); 123 124 /** 125 * This callback is called when we received SUBSCRIBE request to refresh 104 * @param tsx Transaction. 105 * @param event The event. 106 */ 107 void (*on_tsx_state)(pjsip_evsub *sub, pjsip_transaction *tsx, 108 pjsip_event *event); 109 110 /** 111 * This callback is called when incoming SUBSCRIBE (or any method that 112 * establishes the subscription in the first place) is received. It 113 * allows application to specify what response should be sent to 114 * remote, along with additional headers and message body to be put 115 * in the response. 116 * 117 * This callback is OPTIONAL. 118 * 119 * However, implementation MUST send NOTIFY request upon receiving this 120 * callback. The suggested behavior is to call 121 * #pjsip_evsub_last_notify(), since this function takes care 122 * about unsubscription request and calculates the appropriate expiration 123 * interval. 124 */ 125 void (*on_rx_refresh)( pjsip_evsub *sub, 126 pjsip_rx_data *rdata, 127 int *p_st_code, 128 pj_str_t **p_st_text, 129 pjsip_hdr *res_hdr, 130 pjsip_msg_body **p_body); 131 132 /** 133 * This callback is called when client/subscriber received incoming 134 * NOTIFY request. It allows the application to specify what response 135 * should be sent to remote, along with additional headers and message 136 * body to be put in the response. 137 * 138 * This callback is OPTIONAL. When it is not implemented, the default 139 * behavior is to respond incoming NOTIFY request with 200 (OK). 140 * 141 * @param sub The subscription instance. 142 * @param rdata The received NOTIFY request. 143 * @param p_st_code Application MUST set the value of this argument with 144 * final status code (200-699) upon returning from the 145 * callback. 146 * @param p_st_text Custom status text, if any. 147 * @param res_hdr Upon return, application can put additional headers 148 * to be sent in the response in this list. 149 * @param p_body Application MAY specify message body to be sent in 150 * the response. 151 */ 152 void (*on_rx_notify)(pjsip_evsub *sub, 153 pjsip_rx_data *rdata, 154 int *p_st_code, 155 pj_str_t **p_st_text, 156 pjsip_hdr *res_hdr, 157 pjsip_msg_body **p_body); 158 159 /** 160 * This callback is called when it is time for the client to refresh 126 161 * the subscription. 127 162 * 163 * This callback is OPTIONAL. When it is not implemented, the default 164 * behavior is to refresh subscription by sending SUBSCRIBE with the 165 * interval set to current/last interval. 166 * 128 167 * @param sub The subscription instance. 129 * @param rdata The received SUBSCRIBE request. 130 */ 131 void (*on_received_refresh)(pjsip_event_sub *sub, pjsip_rx_data *rdata); 132 133 /** 134 * This callback is called when the module receives final response on 135 * previously sent SUBSCRIBE request. 136 * 137 * @param sub The subscription instance. 138 * @param event The event. 139 */ 140 void (*on_received_sub_response)(pjsip_event_sub *sub, pjsip_event *event); 141 142 /** 143 * This callback is called when the module receives incoming NOTIFY 144 * request. 145 * 146 * @param sub The subscription instance. 147 * @param rdata The received data. 148 */ 149 void (*on_received_notify)(pjsip_event_sub *sub, pjsip_rx_data *rdata); 150 151 /** 152 * This callback is called when the module receives final response to 153 * previously sent NOTIFY request. 154 * 155 * @param sub The subscription instance. 156 * @param event The event. 157 */ 158 void (*on_received_notify_response)(pjsip_event_sub *sub, pjsip_event *event); 168 */ 169 void (*on_client_refresh)(pjsip_evsub *sub); 170 171 /** 172 * This callback is called when server doesn't receive subscription 173 * refresh after the specified subscription interval. 174 * 175 * This callback is OPTIONAL. When it is not implemented, the default 176 * behavior is to send NOTIFY to terminate the subscription. 177 */ 178 void (*on_server_timeout)(pjsip_evsub *sub); 159 179 160 180 }; 161 181 162 /** 163 * This structure describes an event subscription record. The structure is used 164 * to represent both subscriber and notifier. 165 */ 166 struct pjsip_event_sub 167 { 168 pj_pool_t *pool; /**< Pool. */ 169 pjsip_endpoint *endpt; /**< Endpoint. */ 170 pjsip_event_sub_cb cb; /**< Callback. */ 171 pj_mutex_t *mutex; /**< Mutex. */ 172 pjsip_role_e role; /**< Role (UAC=subscriber, UAS=notifier) */ 173 pjsip_event_sub_state state; /**< Subscription state. */ 174 pj_str_t state_str; /**< String describing the state. */ 175 pjsip_from_hdr *from; /**< Cached local info (From) */ 176 pjsip_to_hdr *to; /**< Cached remote info (To) */ 177 pjsip_contact_hdr *contact; /**< Cached local contact. */ 178 pjsip_cid_hdr *call_id; /**< Cached Call-ID */ 179 int cseq; /**< Outgoing CSeq */ 180 pjsip_event_hdr *event; /**< Event description. */ 181 pjsip_expires_hdr *uac_expires; /**< Cached Expires header (UAC only). */ 182 pjsip_accept_hdr *local_accept; /**< Local Accept header. */ 183 pjsip_route_hdr route_set; /**< Route-set. */ 184 185 pj_str_t key; /**< Key in the hash table. */ 186 void *user_data; /**< Application data. */ 187 int default_interval; /**< Refresh interval. */ 188 pj_timer_entry timer; /**< Internal timer. */ 189 pj_time_val expiry_time; /**< Time when subscription expires. */ 190 int pending_tsx; /**< Number of pending transactions. */ 191 pj_bool_t delete_flag; /**< Pending deletion flag. */ 192 193 pjsip_auth_session auth_sess; /**< Authorization sessions. */ 194 unsigned cred_cnt; /**< Number of credentials. */ 195 pjsip_cred_info *cred_info; /**< Array of credentials. */ 196 }; 197 198 199 200 201 /** 202 * Initialize the module and get the instance of the module to be registered to 203 * endpoint. 204 * 205 * @return The module instance. 206 */ 207 PJ_DECL(pjsip_module*) pjsip_event_sub_get_module(void); 208 209 210 /** 211 * Register event package. 212 * 213 * @param event The event identification for the package. 182 183 /** 184 * @see pjsip_evsub_user 185 */ 186 typedef struct pjsip_evsub_user pjsip_evsub_user; 187 188 189 /** 190 * SUBSCRIBE method constant. 191 */ 192 extern const pjsip_method pjsip_subscribe_method; 193 194 /** 195 * NOTIFY method constant. 196 */ 197 extern const pjsip_method pjsip_notify_method; 198 199 200 201 /** 202 * Initialize the event subscription module and register the module to the 203 * specified endpoint. 204 * 205 * @param endpt The endpoint instance. 206 * 207 * @return PJ_SUCCESS if module can be created and registered 208 * successfully. 209 */ 210 PJ_DECL(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt); 211 212 213 /** 214 * Get the event subscription module instance that was previously created 215 * and registered to endpoint. 216 * 217 * @return The event subscription module instance. 218 */ 219 PJ_DECL(pjsip_module*) pjsip_evsub_instance(void); 220 221 222 /** 223 * Register event package to the event subscription framework. 224 * 225 * @param pkg_mod The module that implements the event package being 226 * registered. 227 * @param event_name Event package identification. 228 * @param expires Default subscription expiration time, in seconds. 214 229 * @param accept_cnt Number of strings in Accept array. 215 230 * @param accept Array of Accept value. 216 * @param cb Callback to receive incoming SUBSCRIBE for the package. 217 * 218 * @return Zero on success. 219 */ 220 PJ_DECL(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event_name, 221 int accept_cnt, 222 const pj_str_t accept[], 223 const pjsip_event_sub_pkg_cb *cb ); 224 225 226 /** 227 * Create initial subscription instance (client). 228 * 229 * @param endpt The endpoint. 230 * @param from URL to put in From header. 231 * @param to The target resource. 232 * @param event Event package. 233 * @param expires Expiration time. 234 * @param accept Accept specification. 235 * @param user_data Application data to attach to this subscription. 236 * 237 * @return New client subscription instance. 238 */ 239 PJ_DECL(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, 240 const pj_str_t *from, 241 const pj_str_t *to, 242 const pj_str_t *event, 243 int expires, 244 int accept_cnt, 245 const pj_str_t accept[], 246 void *user_data, 247 const pjsip_event_sub_cb *cb); 248 249 /** 250 * Set credentials to be used for outgoing request messages. 251 * 252 * @param sub Subscription instance. 253 * @param count Number of credentials. 254 * @param cred Array of credential info. 255 * 256 * @return Zero on success. 257 */ 258 PJ_DECL(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, 259 int count, 260 const pjsip_cred_info cred[]); 261 262 /** 263 * Set route set for outgoing requests. 264 * 265 * @param sub Subscription instance. 266 * @param route_set List of route headers. 267 * 268 * @return Zero on success. 269 */ 270 PJ_DECL(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, 271 const pjsip_route_hdr *route_set ); 272 273 274 /** 275 * Send SUBSCRIBE request. 276 * 277 * @param sub Subscription instance. 278 * 279 * @return Zero on success. 280 */ 281 PJ_DECL(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ); 282 283 /** 284 * Terminate subscription (client). This will send unsubscription request to 285 * notifier. 231 * 232 * @return PJ_SUCCESS on success. 233 */ 234 PJ_DECL(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, 235 const pj_str_t *event_name, 236 unsigned expires, 237 unsigned accept_cnt, 238 const pj_str_t accept[]); 239 240 241 /** 242 * Create client subscription session. 243 * 244 * @param dlg The underlying dialog to use. 245 * @param user_cb Callback to receive event subscription notifications. 246 * @param event Event name. 247 * @param p_evsub Pointer to receive event subscription instance. 248 * 249 * @return PJ_SUCCESS on success. 250 */ 251 PJ_DECL(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg, 252 const pjsip_evsub_user *user_cb, 253 const pj_str_t *event, 254 pjsip_evsub **p_evsub); 255 256 /** 257 * Create server subscription session. 258 * 259 * @param dlg The underlying dialog to use. 260 * @param user_cb Callback to receive event subscription notifications. 261 * @param rdata The incoming request that creates the event 262 * subscription, such as SUBSCRIBE or REFER. 263 * @param p_evsub Pointer to receive event subscription instance. 264 * 265 * @return PJ_SUCCESS on success. 266 */ 267 PJ_DECL(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg, 268 const pjsip_evsub_user *user_cb, 269 pjsip_rx_data *rdata, 270 pjsip_evsub **p_evsub); 271 272 273 /** 274 * Get subscription state. 275 * 276 * @param sub Event subscription instance. 277 * 278 * @return Subscription state. 279 */ 280 PJ_DECL(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub); 281 282 283 /** 284 * Get the string representation of the subscription state. 285 * 286 * @param sub Event subscription instance. 287 * 288 * @return NULL terminated string. 289 */ 290 PJ_DECL(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub); 291 292 293 /** 294 * Call this function to create request to initiate subscription, to 295 * refresh subcription, or to request subscription termination. 286 296 * 287 297 * @param sub Client subscription instance. 288 * 289 * @return Zero on success. 290 */ 291 PJ_DECL(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ); 292 293 294 /** 295 * For notifier, send NOTIFY request to subscriber, and set the state of 296 * the subscription. 298 * @param method The method that establishes the subscription, such as 299 * SUBSCRIBE or REFER. If this argument is NULL, then 300 * SUBSCRIBE will be used. 301 * @param expires Subscription expiration. If the value is set to zero, 302 * this will request unsubscription. If the value is 303 * negative, default expiration as defined by the package 304 * will be used. 305 * @param p_tdata Pointer to receive the request. 306 * 307 * @return PJ_SUCCESS on success. 308 */ 309 PJ_DECL(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub, 310 const pjsip_method *method, 311 pj_int32_t expires, 312 pjsip_tx_data **p_tdata); 313 314 315 /** 316 * Accept the incoming subscription request by sending 2xx response to 317 * incoming SUBSCRIBE request. 318 * 319 * @param sub Server subscription instance. 320 * @param rdata The incoming subscription request message. 321 * @param st_code Status code, which MUST be final response. 322 * @param hdr_list Optional list of headers to be added in the response. 323 * 324 * @return PJ_SUCCESS on success. 325 */ 326 PJ_DECL(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub, 327 pjsip_rx_data *rdata, 328 int st_code, 329 const pjsip_hdr *hdr_list ); 330 331 332 /** 333 * For notifier, create NOTIFY request to subscriber, and set the state 334 * of the subscription. 297 335 * 298 336 * @param sub The server subscription (notifier) instance. 299 337 * @param state New state to set. 338 * @param state_str The state string name, if state contains value other 339 * than active, pending, or terminated. Otherwise this 340 * argument is ignored. 300 341 * @param reason Specify reason if new state is terminated, otherwise 301 342 * put NULL. 302 * @param type Description of content type. 303 * @param body Text body to send with the NOTIFY, or NULL if the 304 * NOTIFY request should not contain any message body. 305 * 306 * @return Zero on success. 307 */ 308 PJ_DECL(pj_status_t) pjsip_event_sub_notify( pjsip_event_sub *sub, 309 pjsip_event_sub_state state, 310 const pj_str_t *reason, 311 pjsip_msg_body *body); 312 313 /** 314 * Destroy subscription instance. 315 * 316 * @param sub The client or server subscription instance. 317 * 318 * @return Zero on success, one if the subscription will be 319 * deleted automatically later, or -1 on error. 320 */ 321 PJ_DECL(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub); 343 * @param p_tdata Pointer to receive request message. 344 * 345 * @return PJ_SUCCESS on success. 346 */ 347 PJ_DECL(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub, 348 pjsip_evsub_state state, 349 const pj_str_t *state_str, 350 const pj_str_t *reason, 351 pjsip_tx_data **p_tdata); 352 353 354 /** 355 * For notifier, create a NOTIFY request that reflects current subscription 356 * status. 357 * 358 * @param sub The server subscription instance. 359 * @param p_tdata Pointer to receive the request messge. 360 * 361 * @return PJ_SUCCESS on success. 362 */ 363 PJ_DECL(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub, 364 pjsip_tx_data **p_tdata ); 365 366 367 368 /** 369 * Send request message. 370 * 371 * @param sub The event subscription object. 372 * @param tdata Request message to be send. 373 * 374 * @return PJ_SUCCESS on success. 375 */ 376 PJ_DECL(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub, 377 pjsip_tx_data *tdata); 378 379 380 381 /** 382 * Get the event subscription instance in the transaction. 383 * 384 * @param tsx The transaction. 385 * 386 * @return The event subscription instance registered in the 387 * transaction, if any. 388 */ 389 PJ_DECL(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx); 390 391 392 /** 393 * Set event subscription's module data. 394 * 395 * @param sub The event subscription. 396 * @param index The module id. 397 * @param data Arbitrary data. 398 */ 399 PJ_DECL(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, 400 void *data ); 401 402 403 /** 404 * Get event subscription's module data. 405 * 406 * @param sub The event subscription. 407 * @param mod_id The module id. 408 * 409 * @return Data previously set at the specified id. 410 */ 411 PJ_DECL(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ); 412 322 413 323 414 … … 328 419 */ 329 420 330 #endif /* __PJSIP_SIMPLE_EV ENT_NOTIFY_H__ */421 #endif /* __PJSIP_SIMPLE_EVSUB_H__ */ -
pjproject/trunk/pjsip/include/pjsip-simple/evsub_msg.h
- Property svn:keywords set to id
r187 r197 42 42 typedef struct pjsip_event_hdr 43 43 { 44 PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr) 44 PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr); 45 45 pj_str_t event_type; /**< Event name. */ 46 46 pj_str_t id_param; /**< Optional event ID parameter. */ 47 pj _str_t other_param; /**< Other parameter, concatenated together. */47 pjsip_param other_param; /**< Other parameter. */ 48 48 } pjsip_event_hdr; 49 49 … … 61 61 * This structure describes Allow-Events header. 62 62 */ 63 typedef struct pjsip_allow_events_hdr 64 { 65 PJSIP_DECL_HDR_MEMBER(struct pjsip_allow_events_hdr) 66 int event_cnt; /**< Number of event names. */ 67 pj_str_t events[PJSIP_MAX_ALLOW_EVENTS]; /**< Event names. */ 68 } pjsip_allow_events_hdr; 63 typedef pjsip_generic_array_hdr pjsip_allow_events_hdr; 69 64 70 65 … … 84 79 typedef struct pjsip_sub_state_hdr 85 80 { 86 PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr) 81 PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr); 87 82 pj_str_t sub_state; /**< Subscription state. */ 88 83 pj_str_t reason_param; /**< Optional termination reason. */ 89 84 int expires_param; /**< Expires param, or -1. */ 90 85 int retry_after; /**< Retry after param, or -1. */ 91 pj _str_t other_param; /**< Other parameter, concatenated together. */86 pjsip_param other_param; /**< Other parameters. */ 92 87 } pjsip_sub_state_hdr; 93 88 … … 104 99 * Initialize parser for event notify module. 105 100 */ 106 PJ_DEF(void) pjsip_ev ent_notify_init_parser(void);101 PJ_DEF(void) pjsip_evsub_init_parser(void); 107 102 108 103 -
pjproject/trunk/pjsip/include/pjsip-simple/pidf.h
- Property svn:keywords set to id
r65 r197 24 24 * @brief PIDF/Presence Information Data Format (RFC 3863) 25 25 */ 26 #include <pj /types.h>27 #include <pj /xml.h>26 #include <pjsip-simple/types.h> 27 #include <pjlib-util/xml.h> 28 28 29 29 PJ_BEGIN_DECL -
pjproject/trunk/pjsip/include/pjsip-simple/presence.h
- Property svn:keywords set to id
r65 r197 24 24 * @brief SIP Extension for Presence (RFC 3856) 25 25 */ 26 #include <pjsip _simple/event_notify.h>27 #include <pjsip _simple/pidf.h>28 #include <pjsip _simple/xpidf.h>26 #include <pjsip-simple/evsub.h> 27 #include <pjsip-simple/pidf.h> 28 #include <pjsip-simple/xpidf.h> 29 29 30 30 … … 39 39 * This module contains the implementation of SIP Presence Extension as 40 40 * described in RFC 3856. It uses the SIP Event Notification framework 41 * (ev ent_notify.h) and extends the framework by implementing "presence"41 * (evsub.h) and extends the framework by implementing "presence" 42 42 * event package. 43 43 */ 44 44 45 /** 46 * Presence message body type. 47 */ 48 typedef enum pjsip_pres_type 45 46 47 /** 48 * Initialize the presence module and register it as endpoint module and 49 * package to the event subscription module. 50 * 51 * @param endpt The endpoint instance. 52 * @param mod_evsub The event subscription module instance. 53 * 54 * @return PJ_SUCCESS if the module is successfully 55 * initialized and registered to both endpoint 56 * and the event subscription module. 57 */ 58 PJ_DECL(pj_status_t) pjsip_pres_init_module(pjsip_endpoint *endpt, 59 pjsip_module *mod_evsub); 60 61 62 /** 63 * Get the presence module instance. 64 * 65 * @return The presence module instance. 66 */ 67 PJ_DECL(pjsip_module*) pjsip_pres_instance(void); 68 69 70 #define PJSIP_PRES_STATUS_MAX_INFO 8 71 72 /** 73 * This structure describes presence status of a presentity. 74 */ 75 struct pjsip_pres_status 49 76 { 50 PJSIP_PRES_TYPE_PIDF, 51 PJSIP_PRES_TYPE_XPIDF, 52 } pjsip_pres_type; 53 54 /** 55 * This structure describe a presentity, for both subscriber and notifier. 56 */ 57 typedef struct pjsip_presentity 58 { 59 pjsip_event_sub *sub; /**< Event subscribtion record. */ 60 pjsip_pres_type pres_type; /**< Presentity type. */ 61 pjsip_msg_body *uas_body; /**< Message body (UAS only). */ 62 union { 63 pjpidf_pres *pidf; 64 pjxpidf_pres *xpidf; 65 } uas_data; /**< UAS data. */ 66 pj_str_t timestamp; /**< Time of last update. */ 67 void *user_data; /**< Application data. */ 68 } pjsip_presentity; 69 70 71 /** 72 * This structure describe callback that is registered to receive notification 73 * from the presence module. 74 */ 75 typedef struct pjsip_presence_cb 76 { 77 /** 78 * This callback is first called when the module receives incoming 79 * SUBSCRIBE request to determine whether application wants to accept 80 * the request. If it does, then on_presence_request will be called. 81 * 82 * @param rdata The received message. 83 * @return Application should return 2xx to accept the request, 84 * or failure status (>=300) to reject the request. 85 */ 86 void (*accept_presence)(pjsip_rx_data *rdata, int *status); 87 88 /** 89 * This callback is called when the module receive the first presence 90 * subscription request. 91 * 92 * @param pres The presence descriptor. 93 * @param rdata The incoming request. 94 * @param timeout Timeout to be set for incoming request. Otherwise 95 * app can just leave this and accept the default. 96 */ 97 void (*on_received_request)(pjsip_presentity *pres, pjsip_rx_data *rdata, 98 int *timeout); 99 100 /** 101 * This callback is called when the module received subscription refresh 102 * request. 103 * 104 * @param pres The presence descriptor. 105 * @param rdata The incoming request. 106 */ 107 void (*on_received_refresh)(pjsip_presentity *pres, pjsip_rx_data *rdata); 108 109 /** 110 * This callback is called when the module receives incoming NOTIFY 111 * request. 112 * 113 * @param pres The presence descriptor. 114 * @param open The latest status of the presentity. 115 */ 116 void (*on_received_update)(pjsip_presentity *pres, pj_bool_t open); 117 118 /** 119 * This callback is called when the subscription has terminated. 120 * 121 * @param sub The subscription instance. 122 * @param reason The termination reason. 123 */ 124 void (*on_terminated)(pjsip_presentity *pres, const pj_str_t *reason); 125 126 } pjsip_presence_cb; 127 128 129 /** 130 * Initialize the presence module and register callback. 131 * 132 * @param cb Callback structure. 133 */ 134 PJ_DECL(void) pjsip_presence_init(const pjsip_presence_cb *cb); 135 136 137 /** 138 * Create to presence subscription of a presentity URL. 139 * 140 * @param endpt Endpoint instance. 141 * @param local_url Local URL. 142 * @param remote_url Remote URL which the presence is being subscribed. 143 * @param expires The expiration. 144 * @param user_data User data to attach to presence subscription. 145 * 146 * @return The presence structure if successfull, or NULL if 147 * failed. 148 */ 149 PJ_DECL(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, 150 const pj_str_t *local_url, 151 const pj_str_t *remote_url, 152 int expires, 153 void *user_data ); 154 155 /** 156 * Set credentials to be used by this presentity for outgoing requests. 157 * 158 * @param pres Presentity instance. 159 * @param count Number of credentials in the array. 160 * @param cred Array of credentials. 161 * 162 * @return Zero on success. 163 */ 164 PJ_DECL(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, 165 int count, 166 const pjsip_cred_info cred[]); 167 168 /** 169 * Set route set for outgoing requests. 170 * 171 * @param pres Presentity instance. 172 * @param route_set List of route headers. 173 * 174 * @return Zero on success. 175 */ 176 PJ_DECL(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, 177 const pjsip_route_hdr *hdr ); 178 179 /** 180 * Send SUBSCRIBE request for the specified presentity. 181 * 182 * @param pres The presentity instance. 183 * 184 * @return Zero on success. 185 */ 186 PJ_DECL(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ); 187 188 /** 189 * Ceased the presence subscription. 190 * 191 * @param pres The presence structure. 192 * 193 * @return Zero on success. 194 */ 195 PJ_DECL(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ); 196 197 /** 198 * Notify subscriber about change in local status. 199 * 200 * @param pres The presence structure. 201 * @param state Set the state of the subscription. 202 * @param open Set the presence status (open or closed). 203 * 204 * @return Zero if a NOTIFY request can be sent. 205 */ 206 PJ_DECL(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, 207 pjsip_event_sub_state state, 208 pj_bool_t open ); 209 210 /** 211 * Destroy presence structure and the underlying subscription. 212 * 213 * @param pres The presence structure. 214 * 215 * @return Zero if the subscription was destroyed, or one if 216 * the subscription can not be destroyed immediately 217 * and will be destroyed later, or -1 if failed. 218 */ 219 PJ_DECL(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ); 77 unsigned info_cnt; /**< Number of info in the status. */ 78 struct { 79 80 pj_bool_t basic_open; /**< Basic status/availability. */ 81 pj_str_t id; /**< Tuple id. */ 82 pj_str_t contact; /**< Optional contact address. */ 83 84 } info[PJSIP_PRES_STATUS_MAX_INFO]; /**< Array of info. */ 85 86 pj_bool_t _is_valid; /**< Internal flag. */ 87 }; 88 89 90 /** 91 * @see pjsip_pres_status 92 */ 93 typedef struct pjsip_pres_status pjsip_pres_status; 94 95 96 /** 97 * Create presence client subscription session. 98 * 99 * @param dlg The underlying dialog to use. 100 * @param user_cb Pointer to callbacks to receive presence subscription 101 * events. 102 * @param p_evsub Pointer to receive the presence subscription 103 * session. 104 * 105 * @return PJ_SUCCESS on success. 106 */ 107 PJ_DECL(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, 108 const pjsip_evsub_user *user_cb, 109 pjsip_evsub **p_evsub ); 110 111 112 /** 113 * Create presence server subscription session. 114 * 115 * @param dlg The underlying dialog to use. 116 * @param user_cb Pointer to callbacks to receive presence subscription 117 * events. 118 * @param rdata The incoming SUBSCRIBE request that creates the event 119 * subscription. 120 * @param p_evsub Pointer to receive the presence subscription 121 * session. 122 * 123 * @return PJ_SUCCESS on success. 124 */ 125 PJ_DECL(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, 126 const pjsip_evsub_user *user_cb, 127 pjsip_rx_data *rdata, 128 pjsip_evsub **p_evsub ); 129 130 131 /** 132 * Call this function to create request to initiate presence subscription, to 133 * refresh subcription, or to request subscription termination. 134 * 135 * @param sub Client subscription instance. 136 * @param expires Subscription expiration. If the value is set to zero, 137 * this will request unsubscription. 138 * @param p_tdata Pointer to receive the request. 139 * 140 * @return PJ_SUCCESS on success. 141 */ 142 PJ_DECL(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, 143 pj_int32_t expires, 144 pjsip_tx_data **p_tdata); 145 146 147 148 /** 149 * Accept the incoming subscription request by sending 2xx response to 150 * incoming SUBSCRIBE request. 151 * 152 * @param sub Server subscription instance. 153 * @param rdata The incoming subscription request message. 154 * @param st_code Status code, which MUST be final response. 155 * @param hdr_list Optional list of headers to be added in the response. 156 * 157 * @return PJ_SUCCESS on success. 158 */ 159 PJ_DECL(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, 160 pjsip_rx_data *rdata, 161 int st_code, 162 const pjsip_hdr *hdr_list ); 163 164 165 166 167 /** 168 * For notifier, create NOTIFY request to subscriber, and set the state 169 * of the subscription. Application MUST set the presence status to the 170 * appropriate state (by calling #pjsip_pres_set_status()) before calling 171 * this function. 172 * 173 * @param sub The server subscription (notifier) instance. 174 * @param state New state to set. 175 * @param state_str The state string name, if state contains value other 176 * than active, pending, or terminated. Otherwise this 177 * argument is ignored. 178 * @param reason Specify reason if new state is terminated, otherwise 179 * put NULL. 180 * @param p_tdata Pointer to receive the request. 181 * 182 * @return PJ_SUCCESS on success. 183 */ 184 PJ_DECL(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub, 185 pjsip_evsub_state state, 186 const pj_str_t *state_str, 187 const pj_str_t *reason, 188 pjsip_tx_data **p_tdata); 189 190 191 /** 192 * Create NOTIFY request to reflect current subscription status. 193 * 194 * @param sub Server subscription object. 195 * @param p_tdata Pointer to receive request. 196 * 197 * @return PJ_SUCCESS on success. 198 */ 199 PJ_DECL(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, 200 pjsip_tx_data **p_tdata ); 201 202 203 204 /** 205 * Send request. 206 * 207 * @param sub The subscription object. 208 * @param tdata Request message to be sent. 209 * 210 * @return PJ_SUCCESS on success. 211 */ 212 PJ_DECL(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, 213 pjsip_tx_data *tdata ); 214 215 216 /** 217 * Get the presence status. Client normally would call this function 218 * after receiving NOTIFY request from server. 219 * 220 * @param sub The client or server subscription. 221 * @param status The structure to receive presence status. 222 * 223 * @return PJ_SUCCESS on success. 224 */ 225 PJ_DECL(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, 226 pjsip_pres_status *status ); 227 228 229 /** 230 * Set the presence status. This operation is only valid for server 231 * subscription. After calling this function, application would need to 232 * send NOTIFY request to client. 233 * 234 * @param sub The server subscription. 235 * @param status Status to be set. 236 * 237 * @return PJ_SUCCESS on success. 238 */ 239 PJ_DECL(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, 240 const pjsip_pres_status *status ); 220 241 221 242 -
pjproject/trunk/pjsip/include/pjsip-simple/xpidf.h
- Property svn:keywords set to id
r65 r197 24 24 * @brief XPIDF/Presence Information Data Format 25 25 */ 26 #include <pj /types.h>27 #include <pj /xml.h>26 #include <pjsip-simple/types.h> 27 #include <pjlib-util/xml.h> 28 28 29 29 PJ_BEGIN_DECL -
pjproject/trunk/pjsip/include/pjsip/sip_config.h
r127 r197 34 34 #define PJSIP_POOL_LEN_TRANSPORT 512 35 35 #define PJSIP_POOL_INC_TRANSPORT 512 36 #define PJSIP_POOL_LEN_TDATA 250037 #define PJSIP_POOL_INC_TDATA 51236 #define PJSIP_POOL_LEN_TDATA 4000 37 #define PJSIP_POOL_INC_TDATA 4000 38 38 #define PJSIP_POOL_LEN_UA (64 + 32*PJSIP_MAX_DIALOG_COUNT) 39 39 #define PJSIP_POOL_INC_UA 0 … … 80 80 81 81 /* Module related constants. */ 82 #define PJSIP_MAX_MODULE 882 #define PJSIP_MAX_MODULE 16 83 83 84 84 /* Maximum header types. */ -
pjproject/trunk/pjsip/include/pjsip/sip_errno.h
r162 r197 208 208 */ 209 209 #define PJSIP_ETSXDESTROYED (PJSIP_ERRNO_START_PJSIP + 70) /* 171070 */ 210 /** 211 * @hideinitializer 212 * No transaction. 213 */ 214 #define PJSIP_ENOTSX (PJSIP_ERRNO_START_PJSIP + 71) /* 171071 */ 210 215 211 216 -
pjproject/trunk/pjsip/include/pjsip/sip_msg.h
r145 r197 412 412 PJSIP_SC_REQUEST_TERMINATED = 487, 413 413 PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, 414 PJSIP_SC_ UNKNOWN_EVENT = 489,414 PJSIP_SC_BAD_EVENT = 489, 415 415 PJSIP_SC_REQUEST_UPDATED = 490, 416 416 PJSIP_SC_REQUEST_PENDING = 491, … … 499 499 * must fill in all members of this structure. 500 500 */ 501 typedefstruct pjsip_msg_body501 struct pjsip_msg_body 502 502 { 503 503 /** MIME content type. … … 566 566 void* (*clone_data)(pj_pool_t *pool, const void *data, unsigned len); 567 567 568 } pjsip_msg_body;568 }; 569 569 570 570 /** -
pjproject/trunk/pjsip/include/pjsip/sip_types.h
r171 r197 93 93 94 94 /** 95 * Forward declaration for message body (sip_msg.h). 96 */ 97 typedef struct pjsip_msg_body pjsip_msg_body; 98 99 /** 95 100 * Forward declaration for header field (sip_msg.h). 96 101 */ … … 144 149 typedef enum pjsip_role_e 145 150 { 146 PJSIP_ROLE_UAC, /**< Transaction role is UAC. */ 147 PJSIP_ROLE_UAS, /**< Transaction role is UAS. */ 151 PJSIP_ROLE_UAC, /**< Role is UAC. */ 152 PJSIP_ROLE_UAS, /**< Role is UAS. */ 153 154 /* Alias: */ 155 156 PJSIP_UAC_ROLE = PJSIP_ROLE_UAC, /**< Role is UAC. */ 157 PJSIP_UAS_ROLE = PJSIP_ROLE_UAS, /**< Role is UAS. */ 158 148 159 } pjsip_role_e; 149 160 -
pjproject/trunk/pjsip/include/pjsip_simple.h
- Property svn:keywords changed from Id to id
r66 r197 34 34 #define __PJSIP_SIMPLE_H__ 35 35 36 #include <pjsip _simple/messaging.h>37 #include <pjsip _simple/event_notify.h>38 #include <pjsip _simple/pidf.h>39 #include <pjsip _simple/presence.h>36 #include <pjsip-simple/evsub.h> 37 #include <pjsip-simple/presence.h> 38 #include <pjsip-simple/pidf.h> 39 #include <pjsip-simple/xpidf.h> 40 40 41 41 #endif /* __PJSIP_SIMPLE_H__ */ -
pjproject/trunk/pjsip/src/pjsip-simple/evsub.c
- Property svn:keywords set to id
r187 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip_simple/event_notify.h> 20 #include <pjsip/sip_msg.h> 21 #include <pjsip/sip_util.h> 19 #include <pjsip-simple/evsub.h> 20 #include <pjsip-simple/evsub_msg.h> 21 #include <pjsip-simple/errno.h> 22 #include <pjsip/sip_errno.h> 23 #include <pjsip/sip_module.h> 22 24 #include <pjsip/sip_endpoint.h> 23 #include <pjsip/sip_module.h> 25 #include <pjsip/sip_dialog.h> 26 #include <pjsip/sip_auth.h> 24 27 #include <pjsip/sip_transaction.h> 25 28 #include <pjsip/sip_event.h> 29 #include <pj/assert.h> 30 #include <pj/guid.h> 31 #include <pj/log.h> 32 #include <pj/os.h> 26 33 #include <pj/pool.h> 27 #include <pj/timer.h>28 34 #include <pj/string.h> 29 #include <pj/hash.h> 30 #include <pj/os.h> 31 #include <pj/except.h> 32 #include <pj/log.h> 33 #include <pj/guid.h> 34 35 #define THIS_FILE "event_sub" 36 37 /* String names for state. 38 * The names here should be compliant with sub_state names in RFC3265. 39 */ 40 static const pj_str_t state[] = { 41 { "null", 4 }, 42 { "active", 6 }, 43 { "pending", 7 }, 44 { "terminated", 10 }, 45 { "unknown", 7 } 35 36 37 #define THIS_FILE "evsub.c" 38 39 /* 40 * Global constant 41 */ 42 43 /* Let's define this enum, so that it'll trigger compilation error 44 * when somebody define the same enum in sip_msg.h 45 */ 46 enum 47 { 48 PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD, 49 PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD 46 50 }; 47 51 48 /* Timer IDs */ 49 #define TIMER_ID_REFRESH 1 50 #define TIMER_ID_UAS_EXPIRY 2 51 52 /* Static configuration. */ 53 #define SECONDS_BEFORE_EXPIRY 10 54 #define MGR_POOL_SIZE 512 55 #define MGR_POOL_INC 0 56 #define SUB_POOL_SIZE 2048 57 #define SUB_POOL_INC 0 58 #define HASH_TABLE_SIZE 32 52 const pjsip_method pjsip_subscribe_method = 53 { 54 PJSIP_SUBSCRIBE_METHOD, 55 { "SUBSCRIBE", 9 } 56 }; 57 58 const pjsip_method pjsip_notify_method = 59 { 60 PJSIP_NOTIFY_METHOD, 61 { "NOTIFY", 6 } 62 }; 63 64 /* 65 * Static prototypes. 66 */ 67 static void mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*); 68 static pj_status_t mod_evsub_unload(void); 69 70 71 /* 72 * State names. 73 */ 74 static pj_str_t evsub_state_names[] = 75 { 76 { "NULL", 4}, 77 { "SENT", 4}, 78 { "ACCEPTED", 8}, 79 { "PENDING", 7}, 80 { "ACTIVE", 6}, 81 { "TERMINATED", 10}, 82 { "UNKNOWN", 7} 83 }; 84 85 /* 86 * Timer constants. 87 */ 88 89 /* Number of seconds to send SUBSCRIBE before the actual expiration */ 90 #define TIME_UAC_REFRESH 5 91 92 /* Time to wait for the final NOTIFY after sending unsubscription */ 93 #define TIME_UAC_TERMINATE 5 94 95 /* If client responds NOTIFY with non-2xx final response (such as 401), 96 * wait for this seconds for further NOTIFY, otherwise client will 97 * unsubscribe 98 */ 99 #define TIME_UAC_WAIT_NOTIFY 5 100 101 102 /* 103 * Timer id 104 */ 105 enum timer_id 106 { 107 /* No timer. */ 108 TIMER_TYPE_NONE, 109 110 /* Time to refresh client subscription. 111 * The action is to call on_client_refresh() callback. 112 */ 113 TIMER_TYPE_UAC_REFRESH, 114 115 /* UAS timeout after to subscription refresh. 116 * The action is to call on_server_timeout() callback. 117 */ 118 TIMER_TYPE_UAS_TIMEOUT, 119 120 /* UAC waiting for final NOTIFY after unsubscribing 121 * The action is to terminate. 122 */ 123 TIMER_TYPE_UAC_TERMINATE, 124 125 /* UAC waiting for further NOTIFY after sending non-2xx response to 126 * NOTIFY. The action is to unsubscribe. 127 */ 128 TIMER_TYPE_UAC_WAIT_NOTIFY, 129 130 }; 131 132 static const char *timer_names[] = 133 { 134 "None", 135 "UAC_REFRESH", 136 "UAS_TIMEOUT" 137 "UAC_TERMINATE", 138 "UAC_WAIT_NOTIFY", 139 }; 140 141 /* 142 * Definition of event package. 143 */ 144 struct evpkg 145 { 146 PJ_DECL_LIST_MEMBER(struct evpkg); 147 148 pj_str_t pkg_name; 149 pjsip_module *pkg_mod; 150 unsigned pkg_expires; 151 pjsip_accept_hdr *pkg_accept; 152 }; 153 154 155 /* 156 * Event subscription module (mod-evsub). 157 */ 158 static struct mod_evsub 159 { 160 pjsip_module mod; 161 pj_pool_t *pool; 162 pjsip_endpoint *endpt; 163 struct evpkg pkg_list; 164 pjsip_allow_events_hdr *allow_events_hdr; 165 166 } mod_evsub = 167 { 168 { 169 NULL, NULL, /* prev, next. */ 170 { "mod-evsub", 9 }, /* Name. */ 171 -1, /* Id */ 172 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ 173 NULL, /* User data. */ 174 NULL, /* load() */ 175 NULL, /* start() */ 176 NULL, /* stop() */ 177 &mod_evsub_unload, /* unload() */ 178 NULL, /* on_rx_request() */ 179 NULL, /* on_rx_response() */ 180 NULL, /* on_tx_request. */ 181 NULL, /* on_tx_response() */ 182 &mod_evsub_on_tsx_state, /* on_tsx_state() */ 183 } 184 }; 185 186 187 /* 188 * Event subscription session. 189 */ 190 struct pjsip_evsub 191 { 192 char obj_name[PJ_MAX_OBJ_NAME]; /**< Name. */ 193 pj_pool_t *pool; /**< Pool. */ 194 pjsip_endpoint *endpt; /**< Endpoint instance. */ 195 pjsip_dialog *dlg; /**< Underlying dialog. */ 196 struct evpkg *pkg; /**< The event package. */ 197 pjsip_evsub_user user; /**< Callback. */ 198 pjsip_role_e role; /**< UAC=subscriber, UAS=notifier */ 199 pjsip_evsub_state state; /**< Subscription state. */ 200 pj_str_t state_str; /**< String describing the state. */ 201 pjsip_evsub_state dst_state; /**< Pending state to be set. */ 202 pj_str_t dst_state_str;/**< Pending state to be set. */ 203 pjsip_method method; /**< Method that established subscr.*/ 204 pjsip_event_hdr *event; /**< Event description. */ 205 pjsip_expires_hdr *expires; /**< Expires header */ 206 pjsip_accept_hdr *accept; /**< Local Accept header. */ 207 208 pj_time_val refresh_time; /**< Time to refresh. */ 209 pj_timer_entry timer; /**< Internal timer. */ 210 int pending_tsx; /**< Number of pending transactions.*/ 211 212 void *mod_data[PJSIP_MAX_MODULE]; /**< Module data. */ 213 }; 214 215 216 /* 217 * This is the structure that will be "attached" to dialog. 218 * The purpose is to allow multiple subscriptions inside a dialog. 219 */ 220 struct dlgsub 221 { 222 PJ_DECL_LIST_MEMBER(struct dlgsub); 223 pjsip_evsub *sub; 224 }; 225 59 226 60 227 /* Static vars. */ 61 static int mod_id; 62 static const pjsip_method SUBSCRIBE = { PJSIP_OTHER_METHOD, {"SUBSCRIBE", 9}}; 63 static const pjsip_method NOTIFY = { PJSIP_OTHER_METHOD, { "NOTIFY", 6}}; 64 65 typedef struct package 66 { 67 PJ_DECL_LIST_MEMBER(struct package) 68 pj_str_t event; 69 int accept_cnt; 70 pj_str_t *accept; 71 pjsip_event_sub_pkg_cb cb; 72 } package; 73 74 /* Event subscription manager singleton instance. */ 75 static struct pjsip_event_sub_mgr 76 { 77 pj_pool_t *pool; 78 pj_hash_table_t *ht; 79 pjsip_endpoint *endpt; 80 pj_mutex_t *mutex; 81 pjsip_allow_events_hdr *allow_events; 82 package pkg_list; 83 } mgr; 84 85 /* Fordward declarations for static functions. */ 86 static pj_status_t mod_init(pjsip_endpoint *, pjsip_module *, pj_uint32_t); 87 static pj_status_t mod_deinit(pjsip_module*); 88 static void tsx_handler(pjsip_module*, pjsip_event*); 89 static pjsip_event_sub *find_sub(pjsip_rx_data *); 90 static void on_subscribe_request(pjsip_transaction*, pjsip_rx_data*); 91 static void on_subscribe_response(void *, pjsip_event*); 92 static void on_notify_request(pjsip_transaction *, pjsip_rx_data*); 93 static void on_notify_response(void *, pjsip_event *); 94 static void refresh_timer_cb(pj_timer_heap_t*, pj_timer_entry*); 95 static void uas_expire_timer_cb(pj_timer_heap_t*, pj_timer_entry*); 96 static pj_status_t send_sub_refresh( pjsip_event_sub *sub ); 97 98 /* Module descriptor. */ 99 static pjsip_module event_sub_module = 100 { 101 {"EventSub", 8}, /* Name. */ 102 0, /* Flag */ 103 128, /* Priority */ 104 &mgr, /* User data. */ 105 2, /* Number of methods supported . */ 106 { &SUBSCRIBE, &NOTIFY }, /* Array of methods */ 107 &mod_init, /* init_module() */ 108 NULL, /* start_module() */ 109 &mod_deinit, /* deinit_module() */ 110 &tsx_handler, /* tsx_handler() */ 111 }; 112 113 /* 114 * Module initialization. 115 * This will be called by endpoint when it initializes all modules. 116 */ 117 static pj_status_t mod_init( pjsip_endpoint *endpt, 118 struct pjsip_module *mod, pj_uint32_t id ) 119 { 120 pj_pool_t *pool; 121 122 pool = pjsip_endpt_create_pool(endpt, "esubmgr", MGR_POOL_SIZE, MGR_POOL_INC); 123 if (!pool) 124 return -1; 125 126 /* Manager initialization: create hash table and mutex. */ 127 mgr.pool = pool; 128 mgr.endpt = endpt; 129 mgr.ht = pj_hash_create(pool, HASH_TABLE_SIZE); 130 if (!mgr.ht) 131 return -1; 132 133 mgr.mutex = pj_mutex_create(pool, "esubmgr", PJ_MUTEX_SIMPLE); 134 if (!mgr.mutex) 135 return -1; 136 137 /* Attach manager to module. */ 138 mod->mod_data = &mgr; 139 140 /* Init package list. */ 141 pj_list_init(&mgr.pkg_list); 142 143 /* Init Allow-Events header. */ 144 mgr.allow_events = pjsip_allow_events_hdr_create(mgr.pool); 145 146 /* Save the module ID. */ 147 mod_id = id; 148 149 pjsip_event_notify_init_parser(); 150 return 0; 151 } 152 153 /* 154 * Module deinitialization. 155 * Called by endpoint. 156 */ 157 static pj_status_t mod_deinit( struct pjsip_module *mod ) 158 { 159 pj_mutex_lock(mgr.mutex); 160 pj_mutex_destroy(mgr.mutex); 161 pjsip_endpt_destroy_pool(mgr.endpt, mgr.pool); 162 return 0; 163 } 164 165 /* 166 * This public function is called by application to register callback. 167 * In exchange, the instance of the module is returned. 168 */ 169 PJ_DEF(pjsip_module*) pjsip_event_sub_get_module(void) 170 { 171 return &event_sub_module; 172 } 173 174 /* 175 * Register event package. 176 */ 177 PJ_DEF(pj_status_t) pjsip_event_sub_register_pkg( const pj_str_t *event, 178 int accept_cnt, 179 const pj_str_t accept[], 180 const pjsip_event_sub_pkg_cb *cb ) 181 { 182 package *pkg; 183 int i; 184 185 pj_mutex_lock(mgr.mutex); 186 187 /* Create and register new package. */ 188 pkg = pj_pool_alloc(mgr.pool, sizeof(*pkg)); 189 pj_strdup(mgr.pool, &pkg->event, event); 190 pj_list_insert_before(&mgr.pkg_list, pkg); 191 192 /* Save Accept specification. */ 193 pkg->accept_cnt = accept_cnt; 194 pkg->accept = pj_pool_alloc(mgr.pool, accept_cnt*sizeof(pj_str_t)); 228 static const pj_str_t STR_EVENT = { "Event", 5 }; 229 static const pj_str_t STR_SUB_STATE = { "Subscription-State", 18 }; 230 static const pj_str_t STR_TERMINATED = { "terminated", 10 }; 231 static const pj_str_t STR_ACTIVE = { "active", 6 }; 232 static const pj_str_t STR_PENDING = { "pending", 7 }; 233 static const pj_str_t STR_TIMEOUT = { "timeout", 7}; 234 235 /* 236 * On unload module. 237 */ 238 static pj_status_t mod_evsub_unload(void) 239 { 240 pjsip_endpt_release_pool(mod_evsub.endpt, mod_evsub.pool); 241 mod_evsub.pool = NULL; 242 243 return PJ_SUCCESS; 244 } 245 246 /* 247 * Init and register module. 248 */ 249 PJ_DEF(pj_status_t) pjsip_evsub_init_module(pjsip_endpoint *endpt) 250 { 251 pj_status_t status; 252 253 PJ_ASSERT_RETURN(endpt != NULL, PJ_EINVAL); 254 PJ_ASSERT_RETURN(mod_evsub.mod.id == -1, PJ_EINVALIDOP); 255 256 /* Keep endpoint for future reference: */ 257 mod_evsub.endpt = endpt; 258 259 /* Init event package list: */ 260 pj_list_init(&mod_evsub.pkg_list); 261 262 /* Create pool: */ 263 mod_evsub.pool = pjsip_endpt_create_pool(endpt, "evsub", 4000, 4000); 264 if (!mod_evsub.pool) 265 return PJ_ENOMEM; 266 267 /* Register module: */ 268 status = pjsip_endpt_register_module(endpt, &mod_evsub.mod); 269 if (status != PJ_SUCCESS) 270 goto on_error; 271 272 /* Create Allow-Events header: */ 273 mod_evsub.allow_events_hdr = pjsip_allow_events_hdr_create(mod_evsub.pool); 274 275 /* Register SIP-event specific headers parser: */ 276 pjsip_evsub_init_parser(); 277 278 return PJ_SUCCESS; 279 280 on_error: 281 if (mod_evsub.pool) { 282 pjsip_endpt_release_pool(endpt, mod_evsub.pool); 283 mod_evsub.pool = NULL; 284 } 285 mod_evsub.endpt = NULL; 286 return status; 287 } 288 289 290 /* 291 * Get the instance of the module. 292 */ 293 PJ_DEF(pjsip_module*) pjsip_evsub_instance(void) 294 { 295 PJ_ASSERT_RETURN(mod_evsub.mod.id != -1, NULL); 296 297 return &mod_evsub.mod; 298 } 299 300 301 /* 302 * Get the event subscription instance in the transaction. 303 */ 304 PJ_DEF(pjsip_evsub*) pjsip_tsx_get_evsub(pjsip_transaction *tsx) 305 { 306 return tsx->mod_data[mod_evsub.mod.id]; 307 } 308 309 310 /* 311 * Set event subscription's module data. 312 */ 313 PJ_DEF(void) pjsip_evsub_set_mod_data( pjsip_evsub *sub, unsigned mod_id, 314 void *data ) 315 { 316 PJ_ASSERT_ON_FAIL(mod_id < PJSIP_MAX_MODULE, return); 317 sub->mod_data[mod_id] = data; 318 } 319 320 321 /* 322 * Get event subscription's module data. 323 */ 324 PJ_DEF(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ) 325 { 326 PJ_ASSERT_RETURN(mod_id < PJSIP_MAX_MODULE, NULL); 327 return sub->mod_data[mod_id]; 328 } 329 330 331 /* 332 * Find registered event package with matching name. 333 */ 334 static struct evpkg* find_pkg(const pj_str_t *event_name) 335 { 336 struct evpkg *pkg; 337 338 pkg = mod_evsub.pkg_list.next; 339 while (pkg != &mod_evsub.pkg_list) { 340 341 if (pj_stricmp(&pkg->pkg_name, event_name) == 0) { 342 return pkg; 343 } 344 345 pkg = pkg->next; 346 } 347 348 return NULL; 349 } 350 351 /* 352 * Register an event package 353 */ 354 PJ_DEF(pj_status_t) pjsip_evsub_register_pkg( pjsip_module *pkg_mod, 355 const pj_str_t *event_name, 356 unsigned expires, 357 unsigned accept_cnt, 358 const pj_str_t accept[]) 359 { 360 struct evpkg *pkg; 361 unsigned i; 362 363 PJ_ASSERT_RETURN(pkg_mod && event_name, PJ_EINVAL); 364 PJ_ASSERT_RETURN(accept_cnt < PJ_ARRAY_SIZE(pkg->pkg_accept->values), 365 PJ_ETOOMANY); 366 367 /* Make sure no module with the specified name already registered: */ 368 369 PJ_ASSERT_RETURN(find_pkg(event_name) == NULL, PJSIP_SIMPLE_EPKGEXISTS); 370 371 372 /* Create new event package: */ 373 374 pkg = pj_pool_alloc(mod_evsub.pool, sizeof(struct evpkg)); 375 pkg->pkg_mod = pkg_mod; 376 pkg->pkg_expires = expires; 377 pj_strdup(mod_evsub.pool, &pkg->pkg_name, event_name); 378 379 pkg->pkg_accept = pjsip_accept_hdr_create(mod_evsub.pool); 380 pkg->pkg_accept->count = accept_cnt; 195 381 for (i=0; i<accept_cnt; ++i) { 196 pj_strdup(mgr.pool, &pkg->accept[i], &accept[i]); 197 } 198 199 /* Copy callback. */ 200 pj_memcpy(&pkg->cb, cb, sizeof(*cb)); 201 202 /* Update Allow-Events header. */ 203 pj_assert(mgr.allow_events->event_cnt < PJSIP_MAX_ALLOW_EVENTS); 204 mgr.allow_events->events[mgr.allow_events->event_cnt++] = pkg->event; 205 206 pj_mutex_unlock(mgr.mutex); 207 return 0; 208 } 209 210 /* 211 * Create subscription key (for hash table). 212 */ 213 static void create_subscriber_key( pj_str_t *key, pj_pool_t *pool, 214 pjsip_role_e role, 215 const pj_str_t *call_id, const pj_str_t *from_tag) 216 { 217 char *p; 218 219 p = key->ptr = pj_pool_alloc(pool, call_id->slen + from_tag->slen + 3); 220 *p++ = (role == PJSIP_ROLE_UAS ? 'S' : 'C'); 221 *p++ = '$'; 222 pj_memcpy(p, call_id->ptr, call_id->slen); 223 p += call_id->slen; 224 *p++ = '$'; 225 pj_memcpy(p, from_tag->ptr, from_tag->slen); 226 p += from_tag->slen; 227 228 key->slen = p - key->ptr; 229 } 230 231 232 /* 233 * Create UAC subscription. 234 */ 235 PJ_DEF(pjsip_event_sub*) pjsip_event_sub_create( pjsip_endpoint *endpt, 236 const pj_str_t *from, 237 const pj_str_t *to, 238 const pj_str_t *event, 239 int expires, 240 int accept_cnt, 241 const pj_str_t accept[], 242 void *user_data, 243 const pjsip_event_sub_cb *cb) 244 { 245 pjsip_tx_data *tdata; 246 pj_pool_t *pool; 247 const pjsip_hdr *hdr; 248 pjsip_event_sub *sub; 249 PJ_USE_EXCEPTION; 250 251 PJ_LOG(5,(THIS_FILE, "Creating event subscription %.*s to %.*s", 252 event->slen, event->ptr, to->slen, to->ptr)); 253 254 /* Create pool for the event subscription. */ 255 pool = pjsip_endpt_create_pool(endpt, "esub", SUB_POOL_SIZE, SUB_POOL_INC); 256 if (!pool) { 257 return NULL; 258 } 259 260 /* Init subscription. */ 261 sub = pj_pool_calloc(pool, 1, sizeof(*sub)); 262 sub->pool = pool; 263 sub->endpt = endpt; 264 sub->role = PJSIP_ROLE_UAC; 265 sub->state = PJSIP_EVENT_SUB_STATE_PENDING; 266 sub->state_str = state[sub->state]; 267 sub->user_data = user_data; 268 sub->timer.id = 0; 269 sub->default_interval = expires; 270 pj_memcpy(&sub->cb, cb, sizeof(*cb)); 271 pj_list_init(&sub->auth_sess); 272 pj_list_init(&sub->route_set); 273 sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); 274 if (!sub->mutex) { 275 pjsip_endpt_destroy_pool(endpt, pool); 276 return NULL; 277 } 278 279 /* The easiest way to parse the parameters is to create a dummy request! */ 280 tdata = pjsip_endpt_create_request( endpt, &SUBSCRIBE, to, from, to, from, 281 NULL, -1, NULL); 282 if (!tdata) { 283 pj_mutex_destroy(sub->mutex); 284 pjsip_endpt_destroy_pool(endpt, pool); 285 return NULL; 286 } 287 288 /* 289 * Duplicate headers in the request to our structure. 290 */ 291 PJ_TRY { 292 int i; 293 294 /* From */ 295 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_FROM, NULL); 296 pj_assert(hdr != NULL); 297 sub->from = pjsip_hdr_clone(pool, hdr); 298 299 /* To */ 300 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_TO, NULL); 301 pj_assert(hdr != NULL); 302 sub->to = pjsip_hdr_clone(pool, hdr); 303 304 /* Contact. */ 305 sub->contact = pjsip_contact_hdr_create(pool); 306 sub->contact->uri = sub->from->uri; 307 308 /* Call-ID */ 309 hdr = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CALL_ID, NULL); 310 pj_assert(hdr != NULL); 311 sub->call_id = pjsip_hdr_clone(pool, hdr); 312 313 /* CSeq */ 314 sub->cseq = pj_rand() % 0xFFFF; 315 316 /* Event. */ 317 sub->event = pjsip_event_hdr_create(sub->pool); 318 pj_strdup(pool, &sub->event->event_type, event); 319 320 /* Expires. */ 321 sub->uac_expires = pjsip_expires_hdr_create(pool); 322 sub->uac_expires->ivalue = expires; 323 324 /* Accept. */ 325 sub->local_accept = pjsip_accept_hdr_create(pool); 326 for (i=0; i<accept_cnt && i < PJSIP_MAX_ACCEPT_COUNT; ++i) { 327 sub->local_accept->count++; 328 pj_strdup(sub->pool, &sub->local_accept->values[i], &accept[i]); 329 } 330 331 /* Register to hash table. */ 332 create_subscriber_key( &sub->key, pool, PJSIP_ROLE_UAC, 333 &sub->call_id->id, &sub->from->tag); 334 pj_mutex_lock( mgr.mutex ); 335 pj_hash_set( pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); 336 pj_mutex_unlock( mgr.mutex ); 337 338 } 339 PJ_CATCH_ANY { 340 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): caught exception %d during init", 341 sub, state[sub->state].ptr, PJ_GET_EXCEPTION())); 342 343 pjsip_tx_data_dec_ref(tdata); 344 pj_mutex_destroy(sub->mutex); 345 pjsip_endpt_destroy_pool(endpt, sub->pool); 346 return NULL; 347 } 348 PJ_END; 349 350 /* All set, delete temporary transmit data as we don't need it. */ 351 pjsip_tx_data_dec_ref(tdata); 352 353 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): client created, target=%.*s, event=%.*s", 354 sub, state[sub->state].ptr, 355 to->slen, to->ptr, event->slen, event->ptr)); 356 357 return sub; 358 } 359 360 /* 361 * Set credentials. 362 */ 363 PJ_DEF(pj_status_t) pjsip_event_sub_set_credentials( pjsip_event_sub *sub, 364 int count, 365 const pjsip_cred_info cred[]) 366 { 367 pj_mutex_lock(sub->mutex); 368 if (count > 0) { 369 sub->cred_info = pj_pool_alloc(sub->pool, count*sizeof(pjsip_cred_info)); 370 pj_memcpy( sub->cred_info, cred, count*sizeof(pjsip_cred_info)); 371 } 372 sub->cred_cnt = count; 373 pj_mutex_unlock(sub->mutex); 374 return 0; 375 } 376 377 /* 378 * Set route-set. 379 */ 380 PJ_DEF(pj_status_t) pjsip_event_sub_set_route_set( pjsip_event_sub *sub, 381 const pjsip_route_hdr *route_set ) 382 { 383 const pjsip_route_hdr *hdr; 384 385 pj_mutex_lock(sub->mutex); 386 387 /* Clear existing route set. */ 388 pj_list_init(&sub->route_set); 389 390 /* Duplicate route headers. */ 391 hdr = route_set->next; 392 while (hdr != route_set) { 393 pjsip_route_hdr *new_hdr = pjsip_hdr_clone(sub->pool, hdr); 394 pj_list_insert_before(&sub->route_set, new_hdr); 395 hdr = hdr->next; 396 } 397 398 pj_mutex_unlock(sub->mutex); 399 400 return 0; 401 } 402 403 /* 404 * Send subscribe request. 405 */ 406 PJ_DEF(pj_status_t) pjsip_event_sub_subscribe( pjsip_event_sub *sub ) 407 { 382 pj_strdup(mod_evsub.pool, &pkg->pkg_accept->values[i], &accept[i]); 383 } 384 385 /* Add to package list: */ 386 387 pj_list_push_back(&mod_evsub.pkg_list, pkg); 388 389 /* Add to Allow-Events header: */ 390 391 if (mod_evsub.allow_events_hdr->count != 392 PJ_ARRAY_SIZE(mod_evsub.allow_events_hdr->values)) 393 { 394 mod_evsub.allow_events_hdr->values[mod_evsub.allow_events_hdr->count] = 395 pkg->pkg_name; 396 ++mod_evsub.allow_events_hdr->count; 397 } 398 399 400 /* Done */ 401 402 PJ_LOG(5,(THIS_FILE, "Event pkg \"%.*s\" registered by %.*s", 403 (int)event_name->slen, event_name->ptr, 404 (int)pkg_mod->name.slen, pkg_mod->name.ptr)); 405 406 return PJ_SUCCESS; 407 } 408 409 410 411 /* 412 * Update expiration time. 413 */ 414 static void update_expires( pjsip_evsub *sub, pj_uint32_t interval ) 415 { 416 pj_gettimeofday(&sub->refresh_time); 417 sub->refresh_time.sec += interval; 418 } 419 420 421 /* 422 * Schedule timer. 423 */ 424 static void set_timer( pjsip_evsub *sub, int timer_id, 425 pj_int32_t seconds) 426 { 427 if (sub->timer.id != TIMER_TYPE_NONE) { 428 PJ_LOG(5,(sub->obj_name, "%s %s timer", 429 (timer_id==sub->timer.id ? "Updating" : "Cancelling"), 430 timer_names[sub->timer.id])); 431 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 432 sub->timer.id = TIMER_TYPE_NONE; 433 } 434 435 if (timer_id != TIMER_TYPE_NONE) { 436 pj_time_val timeout; 437 438 PJ_ASSERT_ON_FAIL(seconds > 0, return); 439 440 timeout.sec = seconds; 441 timeout.msec = 0; 442 sub->timer.id = timer_id; 443 444 pjsip_endpt_schedule_timer(sub->endpt, &sub->timer, &timeout); 445 446 PJ_LOG(5,(sub->obj_name, "Timer %s scheduled in %d seconds", 447 timer_names[sub->timer.id], timeout.sec)); 448 } 449 } 450 451 452 /* 453 * Destroy session. 454 */ 455 static void evsub_destroy( pjsip_evsub *sub ) 456 { 457 struct dlgsub *dlgsub_head, *dlgsub; 458 459 PJ_LOG(4,(sub->obj_name, "Subscription destroyed")); 460 461 /* Kill timer */ 462 set_timer(sub, TIMER_TYPE_NONE, 0); 463 464 /* Remote this session from dialog's list of subscription */ 465 dlgsub_head = sub->dlg->mod_data[mod_evsub.mod.id]; 466 dlgsub = dlgsub_head->next; 467 while (dlgsub != dlgsub_head) { 468 469 if (dlgsub->sub == sub) { 470 pj_list_erase(dlgsub); 471 break; 472 } 473 474 dlgsub = dlgsub->next; 475 } 476 477 /* Decrement dialog's session */ 478 pjsip_dlg_dec_session(sub->dlg, &mod_evsub.mod); 479 } 480 481 /* 482 * Set subscription session state. 483 */ 484 static void set_state( pjsip_evsub *sub, pjsip_evsub_state state, 485 const pj_str_t *state_str, pjsip_event *event) 486 { 487 pj_str_t old_state_str = sub->state_str; 488 489 sub->state = state; 490 491 if (state_str && state_str->slen) 492 pj_strdup_with_null(sub->pool, &sub->state_str, state_str); 493 else 494 sub->state_str = evsub_state_names[state]; 495 496 PJ_LOG(4,(sub->obj_name, 497 "Subscription state changed %.*s --> %.*s", 498 (int)old_state_str.slen, 499 old_state_str.ptr, 500 (int)sub->state_str.slen, 501 sub->state_str.ptr)); 502 503 if (sub->user.on_evsub_state) 504 (*sub->user.on_evsub_state)(sub, event); 505 506 if (state == PJSIP_EVSUB_STATE_TERMINATED) { 507 508 if (sub->pending_tsx == 0) { 509 evsub_destroy(sub); 510 } 511 } 512 } 513 514 515 /* 516 * Timer callback. 517 */ 518 static void on_timer( pj_timer_heap_t *timer_heap, 519 struct pj_timer_entry *entry) 520 { 521 pjsip_evsub *sub; 522 int timer_id; 523 524 PJ_UNUSED_ARG(timer_heap); 525 526 sub = entry->user_data; 527 528 pjsip_dlg_inc_lock(sub->dlg); 529 530 timer_id = entry->id; 531 entry->id = TIMER_TYPE_NONE; 532 533 switch (timer_id) { 534 535 case TIMER_TYPE_UAC_REFRESH: 536 /* Time for UAC to refresh subscription */ 537 if (sub->user.on_client_refresh) { 538 (*sub->user.on_client_refresh)(sub); 539 } else { 540 pjsip_tx_data *tdata; 541 pj_status_t status; 542 543 PJ_LOG(5,(sub->obj_name, "Refreshing subscription.")); 544 status = pjsip_evsub_initiate(sub, &sub->method, 545 sub->expires->ivalue, 546 &tdata); 547 if (status == PJ_SUCCESS) 548 pjsip_evsub_send_request(sub, tdata); 549 } 550 break; 551 552 case TIMER_TYPE_UAS_TIMEOUT: 553 /* Refresh from UAC has not been received */ 554 if (sub->user.on_server_timeout) { 555 (*sub->user.on_server_timeout)(sub); 556 } else { 557 pjsip_tx_data *tdata; 558 pj_status_t status; 559 560 PJ_LOG(5,(sub->obj_name, "Timeout waiting for refresh. " 561 "Sending NOTIFY to terminate.")); 562 status = pjsip_evsub_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, 563 NULL, &STR_TIMEOUT, &tdata); 564 if (status == PJ_SUCCESS) 565 pjsip_evsub_send_request(sub, tdata); 566 } 567 break; 568 569 case TIMER_TYPE_UAC_TERMINATE: 570 { 571 pjsip_event event; 572 pj_str_t reason = { "unsubscribing", 13}; 573 574 PJSIP_EVENT_INIT_TIMER(event, entry); 575 PJ_LOG(5,(sub->obj_name, "Timeout waiting for final NOTIFY. " 576 "Terminating..")); 577 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, &event); 578 } 579 break; 580 581 case TIMER_TYPE_UAC_WAIT_NOTIFY: 582 { 583 pjsip_tx_data *tdata; 584 pj_status_t status; 585 586 PJ_LOG(5,(sub->obj_name, 587 "Timeout waiting for subsequent NOTIFY (we did " 588 "send non-2xx response for previous NOTIFY). " 589 "Unsubscribing..")); 590 status = pjsip_evsub_initiate( sub, &sub->method, 0, &tdata); 591 if (status == PJ_SUCCESS) 592 pjsip_evsub_send_request(sub, tdata); 593 } 594 break; 595 596 default: 597 pj_assert(!"Invalid timer id"); 598 } 599 600 pjsip_dlg_dec_lock(sub->dlg); 601 } 602 603 604 /* 605 * Create subscription session, used for both client and notifier. 606 */ 607 static pj_status_t evsub_create( pjsip_dialog *dlg, 608 pjsip_role_e role, 609 const pjsip_evsub_user *user_cb, 610 const pj_str_t *event, 611 pjsip_evsub **p_evsub ) 612 { 613 pjsip_evsub *sub; 614 struct evpkg *pkg; 615 struct dlgsub *dlgsub_head, *dlgsub; 408 616 pj_status_t status; 409 617 410 pj_mutex_lock(sub->mutex); 411 status = send_sub_refresh(sub); 412 pj_mutex_unlock(sub->mutex); 413 618 /* Make sure there's package register for the event name: */ 619 620 pkg = find_pkg(event); 621 if (pkg == NULL) 622 return PJSIP_SIMPLE_ENOPKG; 623 624 625 /* Init attributes: */ 626 627 sub = pj_pool_zalloc(dlg->pool, sizeof(struct pjsip_evsub)); 628 sub->pool = dlg->pool; 629 sub->endpt = dlg->endpt; 630 sub->dlg = dlg; 631 sub->pkg = pkg; 632 sub->role = role; 633 sub->state = PJSIP_EVSUB_STATE_NULL; 634 sub->state_str = evsub_state_names[sub->state]; 635 sub->expires = pjsip_expires_hdr_create(sub->pool, pkg->pkg_expires); 636 sub->accept = pjsip_hdr_clone(sub->pool, pkg->pkg_accept); 637 638 sub->timer.user_data = sub; 639 sub->timer.cb = &on_timer; 640 641 /* Set name. */ 642 pj_snprintf(sub->obj_name, PJ_ARRAY_SIZE(sub->obj_name), 643 "evsub%p", sub); 644 645 646 /* Copy callback, if any: */ 647 if (user_cb) 648 pj_memcpy(&sub->user, user_cb, sizeof(pjsip_evsub_user)); 649 650 651 /* Create Event header: */ 652 sub->event = pjsip_event_hdr_create(sub->pool); 653 pj_strdup(sub->pool, &sub->event->event_type, event); 654 655 656 /* Create subcription list: */ 657 658 dlgsub_head = pj_pool_alloc(sub->pool, sizeof(struct dlgsub)); 659 dlgsub = pj_pool_alloc(sub->pool, sizeof(struct dlgsub)); 660 dlgsub->sub = sub; 661 662 pj_list_init(dlgsub_head); 663 pj_list_push_back(dlgsub_head, dlgsub); 664 665 666 /* Register as dialog usage: */ 667 668 status = pjsip_dlg_add_usage(dlg, &mod_evsub.mod, dlgsub_head); 669 if (status != PJ_SUCCESS) 670 return status; 671 672 673 PJ_LOG(5,(sub->obj_name, "%s subscription created, using dialog %s", 674 (role==PJSIP_ROLE_UAC ? "UAC" : "UAS"), 675 dlg->obj_name)); 676 677 *p_evsub = sub; 678 679 return PJ_SUCCESS; 680 } 681 682 683 684 /* 685 * Create client subscription session. 686 */ 687 PJ_DEF(pj_status_t) pjsip_evsub_create_uac( pjsip_dialog *dlg, 688 const pjsip_evsub_user *user_cb, 689 const pj_str_t *event, 690 pjsip_evsub **p_evsub) 691 { 692 pjsip_evsub *sub; 693 pj_status_t status; 694 695 PJ_ASSERT_RETURN(dlg && event && p_evsub, PJ_EINVAL); 696 697 pjsip_dlg_inc_lock(dlg); 698 status = evsub_create(dlg, PJSIP_UAC_ROLE, user_cb, event, &sub); 699 if (status != PJ_SUCCESS) 700 goto on_return; 701 702 /* Add unique Id to Event header */ 703 pj_create_unique_string(sub->pool, &sub->event->id_param); 704 705 /* Increment dlg session. */ 706 pjsip_dlg_inc_session(sub->dlg, &mod_evsub.mod); 707 708 /* Done */ 709 *p_evsub = sub; 710 711 on_return: 712 pjsip_dlg_dec_lock(dlg); 414 713 return status; 415 714 } 416 715 417 /* 418 * Destroy subscription. 419 * If there are pending transactions, then this will just set the flag. 420 */ 421 PJ_DEF(pj_status_t) pjsip_event_sub_destroy(pjsip_event_sub *sub) 422 { 423 pj_assert(sub != NULL); 424 if (sub == NULL) 425 return -1; 426 427 /* Application must terminate the subscription first. */ 428 pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_NULL || 429 sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); 430 431 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): about to be destroyed", 432 sub, state[sub->state].ptr)); 433 434 pj_mutex_lock(mgr.mutex); 435 pj_mutex_lock(sub->mutex); 436 437 /* Set delete flag. */ 438 sub->delete_flag = 1; 439 440 /* Unregister timer, if any. */ 441 if (sub->timer.id != 0) { 442 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 443 sub->timer.id = 0; 444 } 445 446 if (sub->pending_tsx > 0) { 447 pj_mutex_unlock(sub->mutex); 448 pj_mutex_unlock(mgr.mutex); 449 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): has %d pending, will destroy later", 450 sub, state[sub->state].ptr, 451 sub->pending_tsx)); 452 return 1; 453 } 454 455 /* Unregister from hash table. */ 456 pj_hash_set(sub->pool, mgr.ht, sub->key.ptr, sub->key.slen, NULL); 457 458 /* Destroy. */ 459 pj_mutex_destroy(sub->mutex); 460 pjsip_endpt_destroy_pool(sub->endpt, sub->pool); 461 462 pj_mutex_unlock(mgr.mutex); 463 464 PJ_LOG(4,(THIS_FILE, "event_sub%p: destroyed", sub)); 465 return 0; 466 } 467 468 /* Change state. */ 469 static void sub_set_state( pjsip_event_sub *sub, int new_state) 470 { 471 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): changed state to %s", 472 sub, state[sub->state].ptr, state[new_state].ptr)); 473 sub->state = new_state; 474 sub->state_str = state[new_state]; 475 } 476 477 /* 478 * Refresh subscription. 479 */ 480 static pj_status_t send_sub_refresh( pjsip_event_sub *sub ) 716 717 /* 718 * Create server subscription session from incoming request. 719 */ 720 PJ_DEF(pj_status_t) pjsip_evsub_create_uas( pjsip_dialog *dlg, 721 const pjsip_evsub_user *user_cb, 722 pjsip_rx_data *rdata, 723 pjsip_evsub **p_evsub) 724 { 725 pjsip_evsub *sub; 726 pjsip_transaction *tsx; 727 pjsip_accept_hdr *accept_hdr; 728 pjsip_event_hdr *event_hdr; 729 pjsip_expires_hdr *expires_hdr; 730 pj_status_t status; 731 732 /* Check arguments: */ 733 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); 734 735 /* MUST be request message: */ 736 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, 737 PJSIP_ENOTREQUESTMSG); 738 739 /* Transaction MUST have been created (in the dialog) */ 740 tsx = pjsip_rdata_get_tsx(rdata); 741 PJ_ASSERT_RETURN(tsx != NULL, PJSIP_ENOTSX); 742 743 /* No subscription must have been attached to transaction */ 744 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] == NULL, 745 PJSIP_ETYPEEXISTS); 746 747 /* Package MUST implement on_rx_refresh */ 748 PJ_ASSERT_RETURN(user_cb->on_rx_refresh, PJ_EINVALIDOP); 749 750 /* Request MUST have "Event" header: */ 751 752 event_hdr = (pjsip_event_hdr*) 753 pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); 754 if (event_hdr == NULL) { 755 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); 756 } 757 758 /* Start locking the mutex: */ 759 760 pjsip_dlg_inc_lock(dlg); 761 762 /* Create the session: */ 763 764 status = evsub_create(dlg, PJSIP_UAS_ROLE, user_cb, 765 &event_hdr->event_type, &sub); 766 if (status != PJ_SUCCESS) 767 goto on_return; 768 769 /* Just duplicate Event header from the request */ 770 sub->event = pjsip_hdr_clone(sub->pool, event_hdr); 771 772 /* Set the method: */ 773 pjsip_method_copy(sub->pool, &sub->method, 774 &rdata->msg_info.msg->line.req.method); 775 776 /* Update expiration time according to client request: */ 777 778 expires_hdr = (pjsip_expires_hdr*) 779 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); 780 if (expires_hdr) { 781 sub->expires->ivalue = expires_hdr->ivalue; 782 } 783 784 /* Update time. */ 785 update_expires(sub, sub->expires->ivalue); 786 787 /* Update Accept header: */ 788 789 accept_hdr = (pjsip_accept_hdr*) 790 pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); 791 if (accept_hdr) 792 sub->accept = pjsip_hdr_clone(sub->pool, accept_hdr); 793 794 /* We can start the session: */ 795 796 pjsip_dlg_inc_session(dlg, &mod_evsub.mod); 797 sub->pending_tsx++; 798 tsx->mod_data[mod_evsub.mod.id] = sub; 799 800 801 /* Done. */ 802 *p_evsub = sub; 803 804 805 on_return: 806 pjsip_dlg_dec_lock(dlg); 807 return status; 808 } 809 810 811 /* 812 * Get subscription state. 813 */ 814 PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub) 815 { 816 return sub->state; 817 } 818 819 /* 820 * Get state name. 821 */ 822 PJ_DEF(const char*) pjsip_evsub_get_state_name(pjsip_evsub *sub) 823 { 824 return sub->state_str.ptr; 825 } 826 827 828 /* 829 * Initiate client subscription 830 */ 831 PJ_DEF(pj_status_t) pjsip_evsub_initiate( pjsip_evsub *sub, 832 const pjsip_method *method, 833 pj_int32_t expires, 834 pjsip_tx_data **p_tdata) 481 835 { 482 836 pjsip_tx_data *tdata; 483 837 pj_status_t status; 484 const pjsip_route_hdr *route; 485 486 pj_assert(sub->role == PJSIP_ROLE_UAC); 487 pj_assert(sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED); 488 if (sub->role != PJSIP_ROLE_UAC || 489 sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) 838 839 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL); 840 841 /* Use SUBSCRIBE if method is not specified */ 842 if (method == NULL) 843 method = &pjsip_subscribe_method; 844 845 pjsip_dlg_inc_lock(sub->dlg); 846 847 /* Update method: */ 848 if (sub->state == PJSIP_EVSUB_STATE_NULL) 849 pjsip_method_copy(sub->pool, &sub->method, method); 850 851 status = pjsip_dlg_create_request( sub->dlg, method, -1, &tdata); 852 if (status != PJ_SUCCESS) 853 goto on_return; 854 855 856 /* Add Event header: */ 857 pjsip_msg_add_hdr( tdata->msg, 858 pjsip_hdr_shallow_clone(tdata->pool, sub->event)); 859 860 /* Update and add expires header: */ 861 if (expires >= 0) 862 sub->expires->ivalue = expires; 863 pjsip_msg_add_hdr( tdata->msg, 864 pjsip_hdr_shallow_clone(tdata->pool, sub->expires)); 865 866 /* Add Accept header: */ 867 pjsip_msg_add_hdr( tdata->msg, 868 pjsip_hdr_shallow_clone(tdata->pool, sub->accept)); 869 870 871 /* Add Allow-Events header: */ 872 pjsip_msg_add_hdr( tdata->msg, 873 pjsip_hdr_shallow_clone(tdata->pool, 874 mod_evsub.allow_events_hdr)); 875 876 877 *p_tdata = tdata; 878 879 880 on_return: 881 882 pjsip_dlg_dec_lock(sub->dlg); 883 return status; 884 } 885 886 887 /* 888 * Accept incoming subscription request. 889 */ 890 PJ_DEF(pj_status_t) pjsip_evsub_accept( pjsip_evsub *sub, 891 pjsip_rx_data *rdata, 892 int st_code, 893 const pjsip_hdr *hdr_list ) 894 { 895 pjsip_tx_data *tdata; 896 pjsip_transaction *tsx; 897 pj_status_t status; 898 899 /* Check arguments */ 900 PJ_ASSERT_RETURN(sub && rdata, PJ_EINVAL); 901 902 /* Can only be for server subscription: */ 903 PJ_ASSERT_RETURN(sub->role == PJSIP_ROLE_UAS, PJ_EINVALIDOP); 904 905 /* Only expect 2xx status code (for now) */ 906 PJ_ASSERT_RETURN(st_code/100 == 2, PJ_EINVALIDOP); 907 908 /* Subscription MUST have been attached to the transaction. 909 * Initial subscription request will be attached on evsub_create_uas(), 910 * while subsequent requests will be attached in tsx_state() 911 */ 912 tsx = pjsip_rdata_get_tsx(rdata); 913 PJ_ASSERT_RETURN(tsx->mod_data[mod_evsub.mod.id] != NULL, 914 PJ_EINVALIDOP); 915 916 /* Lock dialog */ 917 pjsip_dlg_inc_lock(sub->dlg); 918 919 /* Create response: */ 920 status = pjsip_dlg_create_response( sub->dlg, rdata, st_code, NULL, 921 &tdata); 922 if (status != PJ_SUCCESS) 923 goto on_return; 924 925 926 /* Add expires header: */ 927 pjsip_msg_add_hdr( tdata->msg, 928 pjsip_hdr_shallow_clone(tdata->pool, sub->expires)); 929 930 931 /* Send the response: */ 932 status = pjsip_dlg_send_response( sub->dlg, tsx, tdata ); 933 if (status != PJ_SUCCESS) 934 goto on_return; 935 936 937 on_return: 938 939 pjsip_dlg_dec_lock(sub->dlg); 940 return status; 941 } 942 943 944 /* 945 * Create Subscription-State header based on current server subscription 946 * state. 947 */ 948 static pjsip_sub_state_hdr* sub_state_create( pj_pool_t *pool, 949 pjsip_evsub *sub, 950 pjsip_evsub_state state, 951 const pj_str_t *state_str, 952 const pj_str_t *reason ) 953 { 954 pjsip_sub_state_hdr *sub_state; 955 pj_time_val now, delay; 956 957 /* Get the remaining time before refresh is required */ 958 pj_gettimeofday(&now); 959 delay = sub->refresh_time; 960 PJ_TIME_VAL_SUB(delay, now); 961 962 /* Create the Subscription-State header */ 963 sub_state = pjsip_sub_state_hdr_create(pool); 964 965 /* Fill up the header */ 966 switch (state) { 967 case PJSIP_EVSUB_STATE_SENT: 968 case PJSIP_EVSUB_STATE_ACCEPTED: 969 pj_assert(!"Invalid state!"); 970 /* Treat as pending */ 971 972 case PJSIP_EVSUB_STATE_PENDING: 973 sub_state->sub_state = STR_PENDING; 974 sub_state->expires_param = delay.sec; 975 break; 976 977 case PJSIP_EVSUB_STATE_ACTIVE: 978 sub_state->sub_state = STR_ACTIVE; 979 sub_state->expires_param = delay.sec; 980 break; 981 982 case PJSIP_EVSUB_STATE_TERMINATED: 983 sub_state->sub_state = STR_TERMINATED; 984 if (reason != NULL) 985 pj_strdup(pool, &sub_state->reason_param, reason); 986 break; 987 988 case PJSIP_EVSUB_STATE_UNKNOWN: 989 pj_assert(state_str != NULL); 990 pj_strdup(pool, &sub_state->sub_state, state_str); 991 break; 992 } 993 994 return sub_state; 995 } 996 997 /* 998 * Create and send NOTIFY request. 999 */ 1000 PJ_DEF(pj_status_t) pjsip_evsub_notify( pjsip_evsub *sub, 1001 pjsip_evsub_state state, 1002 const pj_str_t *state_str, 1003 const pj_str_t *reason, 1004 pjsip_tx_data **p_tdata) 1005 { 1006 pjsip_tx_data *tdata; 1007 pjsip_sub_state_hdr *sub_state; 1008 pj_status_t status; 1009 1010 /* Check arguments. */ 1011 PJ_ASSERT_RETURN(sub!=NULL && p_tdata!=NULL, PJ_EINVAL); 1012 1013 /* Lock dialog. */ 1014 pjsip_dlg_inc_lock(sub->dlg); 1015 1016 /* Create NOTIFY request */ 1017 status = pjsip_dlg_create_request( sub->dlg, &pjsip_notify_method, -1, 1018 &tdata); 1019 if (status != PJ_SUCCESS) 1020 goto on_return; 1021 1022 /* Add Event header */ 1023 pjsip_msg_add_hdr(tdata->msg, 1024 pjsip_hdr_shallow_clone(tdata->pool, sub->event)); 1025 1026 /* Add Subscription-State header */ 1027 sub_state = sub_state_create(tdata->pool, sub, state, state_str, 1028 reason); 1029 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)sub_state); 1030 1031 /* Add Allow-Events header */ 1032 pjsip_msg_add_hdr(tdata->msg, 1033 pjsip_hdr_shallow_clone(tdata->pool, mod_evsub.allow_events_hdr)); 1034 1035 /* Add Authentication headers. */ 1036 pjsip_auth_clt_init_req( &sub->dlg->auth_sess, tdata ); 1037 1038 1039 1040 /* Save destination state. */ 1041 sub->dst_state = state; 1042 if (state_str) 1043 pj_strdup(sub->pool, &sub->dst_state_str, state_str); 1044 else 1045 sub->dst_state_str.slen = 0; 1046 1047 1048 *p_tdata = tdata; 1049 1050 on_return: 1051 /* Unlock dialog */ 1052 pjsip_dlg_dec_lock(sub->dlg); 1053 return status; 1054 } 1055 1056 1057 /* 1058 * Create NOTIFY to reflect current status. 1059 */ 1060 PJ_DEF(pj_status_t) pjsip_evsub_current_notify( pjsip_evsub *sub, 1061 pjsip_tx_data **p_tdata ) 1062 { 1063 return pjsip_evsub_notify( sub, sub->state, &sub->state_str, 1064 NULL, p_tdata ); 1065 } 1066 1067 1068 /* 1069 * Send request. 1070 */ 1071 PJ_DEF(pj_status_t) pjsip_evsub_send_request( pjsip_evsub *sub, 1072 pjsip_tx_data *tdata) 1073 { 1074 pj_status_t status; 1075 1076 /* Must be request message. */ 1077 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_REQUEST_MSG, 1078 PJSIP_ENOTREQUESTMSG); 1079 1080 /* Lock */ 1081 pjsip_dlg_inc_lock(sub->dlg); 1082 1083 /* Send the request. */ 1084 status = pjsip_dlg_send_request(sub->dlg, tdata, NULL); 1085 if (status != PJ_SUCCESS) 1086 goto on_return; 1087 1088 1089 /* Special case for NOTIFY: 1090 * The new state was set in pjsip_evsub_notify(), but we apply the 1091 * new state now, when the request was actually sent. 1092 */ 1093 if (pjsip_method_cmp(&tdata->msg->line.req.method, 1094 &pjsip_notify_method)==0) 490 1095 { 491 return -1; 492 } 493 494 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refreshing subscription", 495 sub, state[sub->state].ptr)); 496 497 /* Create request. */ 498 tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 499 &SUBSCRIBE, 500 sub->to->uri, 501 sub->from, sub->to, 502 sub->contact, sub->call_id, 503 sub->cseq++, 504 NULL); 505 506 if (!tdata) { 507 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh: unable to create tx data!", 508 sub, state[sub->state].ptr)); 509 return -1; 510 } 511 512 pjsip_msg_add_hdr( tdata->msg, 513 pjsip_hdr_shallow_clone(tdata->pool, sub->event)); 514 pjsip_msg_add_hdr( tdata->msg, 515 pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); 516 pjsip_msg_add_hdr( tdata->msg, 517 pjsip_hdr_shallow_clone(tdata->pool, sub->local_accept)); 518 pjsip_msg_add_hdr( tdata->msg, 519 pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); 520 521 /* Authentication */ 522 pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, 523 sub->cred_cnt, sub->cred_info); 524 525 /* Route set. */ 526 route = sub->route_set.next; 527 while (route != &sub->route_set) { 528 pj_list_insert_before( &tdata->msg->hdr, 529 pjsip_hdr_shallow_clone(tdata->pool, route)); 530 route = route->next; 531 } 532 533 /* Send */ 534 status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 535 &on_subscribe_response); 536 if (status == 0) { 537 sub->pending_tsx++; 1096 PJ_ASSERT_ON_FAIL( sub->dst_state!=PJSIP_EVSUB_STATE_NULL, 1097 {goto on_return;}); 1098 1099 set_state(sub, sub->dst_state, 1100 (sub->dst_state_str.slen ? &sub->dst_state_str : NULL), 1101 NULL); 1102 1103 sub->dst_state = PJSIP_EVSUB_STATE_NULL; 1104 sub->dst_state_str.slen = 0; 1105 1106 } 1107 1108 1109 on_return: 1110 pjsip_dlg_dec_lock(sub->dlg); 1111 return status; 1112 } 1113 1114 1115 1116 /* 1117 * Attach subscription session to newly created transaction, if appropriate. 1118 */ 1119 static pjsip_evsub *on_new_transaction( pjsip_transaction *tsx, 1120 pjsip_event *event) 1121 { 1122 /* 1123 * Newly created transaction will not have subscription session 1124 * attached to it. Find the subscription session from the dialog, 1125 * by matching the Event header. 1126 */ 1127 pjsip_dialog *dlg; 1128 pjsip_event_hdr *event_hdr; 1129 pjsip_msg *msg; 1130 struct dlgsub *dlgsub_head, *dlgsub; 1131 pjsip_evsub *sub; 1132 1133 dlg = pjsip_tsx_get_dlg(tsx); 1134 if (!dlg) { 1135 pj_assert(!"Transaction should have a dialog instance!"); 1136 return NULL; 1137 } 1138 1139 switch (event->body.tsx_state.type) { 1140 case PJSIP_EVENT_RX_MSG: 1141 msg = event->body.tsx_state.src.rdata->msg_info.msg; 1142 break; 1143 case PJSIP_EVENT_TX_MSG: 1144 msg = event->body.tsx_state.src.tdata->msg; 1145 break; 1146 default: 1147 if (tsx->role == PJSIP_ROLE_UAC) 1148 msg = tsx->last_tx->msg; 1149 else 1150 msg = NULL; 1151 break; 1152 } 1153 1154 if (!msg) { 1155 pj_assert(!"First transaction event is not TX or RX!"); 1156 return NULL; 1157 } 1158 1159 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL); 1160 if (!event_hdr) { 1161 /* Not subscription related message */ 1162 return NULL; 1163 } 1164 1165 /* Find the subscription in the dialog, based on the content 1166 * of Event header: 1167 */ 1168 1169 dlgsub_head = dlg->mod_data[mod_evsub.mod.id]; 1170 if (dlgsub_head == NULL) { 1171 dlgsub_head = pj_pool_alloc(dlg->pool, sizeof(struct dlgsub)); 1172 pj_list_init(dlgsub_head); 1173 dlg->mod_data[mod_evsub.mod.id] = dlgsub_head; 1174 } 1175 dlgsub = dlgsub_head->next; 1176 1177 while (dlgsub != dlgsub_head) { 1178 1179 /* Match event type and Id */ 1180 if (pj_strcmp(&dlgsub->sub->event->id_param, &event_hdr->id_param)==0 && 1181 pj_stricmp(&dlgsub->sub->event->event_type, &event_hdr->event_type)==0) 1182 { 1183 break; 1184 } 1185 dlgsub = dlgsub->next; 1186 } 1187 1188 if (dlgsub == dlgsub_head) { 1189 /* This could be incoming request to create new subscription */ 1190 PJ_LOG(4,(THIS_FILE, 1191 "Subscription not found for %.*s, event=%.*s;id=%.*s", 1192 (int)event_hdr->event_type.slen, 1193 event_hdr->event_type.ptr, 1194 (int)event_hdr->id_param.slen, 1195 event_hdr->id_param.ptr)); 1196 1197 /* If this is an incoming NOTIFY, reject with 481 */ 1198 if (tsx->state == PJSIP_TSX_STATE_TRYING && 1199 pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) 1200 { 1201 pj_str_t reason = pj_str("Subscription Does Not Exist"); 1202 pjsip_tx_data *tdata; 1203 pj_status_t status; 1204 1205 status = pjsip_dlg_create_response(dlg, 1206 event->body.tsx_state.src.rdata, 1207 481, &reason, 1208 &tdata); 1209 if (status == PJ_SUCCESS) { 1210 status = pjsip_dlg_send_response(dlg, tsx, tdata); 1211 } 1212 } 1213 return NULL; 1214 } 1215 1216 /* Found! */ 1217 sub = dlgsub->sub; 1218 1219 /* Attach session to the transaction */ 1220 tsx->mod_data[mod_evsub.mod.id] = sub; 1221 sub->pending_tsx++; 1222 1223 return sub; 1224 } 1225 1226 1227 /* 1228 * Create response, adding custome headers and msg body. 1229 */ 1230 static pj_status_t create_response( pjsip_evsub *sub, 1231 pjsip_rx_data *rdata, 1232 int st_code, 1233 const pj_str_t *st_text, 1234 const pjsip_hdr *res_hdr, 1235 const pjsip_msg_body *body, 1236 pjsip_tx_data **p_tdata) 1237 { 1238 pjsip_tx_data *tdata; 1239 pjsip_hdr *hdr; 1240 pj_status_t status; 1241 1242 status = pjsip_dlg_create_response(sub->dlg, rdata, 1243 st_code, st_text, &tdata); 1244 if (status != PJ_SUCCESS) 1245 return status; 1246 1247 *p_tdata = tdata; 1248 1249 /* Add response headers. */ 1250 hdr = res_hdr->next; 1251 while (hdr != res_hdr) { 1252 pjsip_msg_add_hdr( tdata->msg, 1253 pjsip_hdr_clone(tdata->pool, hdr)); 1254 hdr = hdr->next; 1255 } 1256 1257 /* Add msg body, if any */ 1258 if (body) { 1259 tdata->msg->body = pj_pool_zalloc(tdata->pool, 1260 sizeof(pjsip_msg_body)); 1261 status = pjsip_msg_body_clone(tdata->pool, 1262 tdata->msg->body, 1263 body); 1264 if (status != PJ_SUCCESS) { 1265 tdata->msg->body = NULL; 1266 /* Ignore */ 1267 return PJ_SUCCESS; 1268 } 1269 } 1270 1271 return PJ_SUCCESS; 1272 } 1273 1274 /* 1275 * Get subscription state from the value of Subscription-State header. 1276 */ 1277 static void get_hdr_state( pjsip_sub_state_hdr *sub_state, 1278 pjsip_evsub_state *state, 1279 pj_str_t **state_str ) 1280 { 1281 if (pj_stricmp(&sub_state->sub_state, &STR_TERMINATED)==0) { 1282 1283 *state = PJSIP_EVSUB_STATE_TERMINATED; 1284 *state_str = NULL; 1285 1286 } else if (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0) { 1287 1288 *state = PJSIP_EVSUB_STATE_ACTIVE; 1289 *state_str = NULL; 1290 1291 } else if (pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0) { 1292 1293 *state = PJSIP_EVSUB_STATE_PENDING; 1294 *state_str = NULL; 1295 538 1296 } else { 539 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to refresh subscription!", 540 sub, state[sub->state].ptr)); 541 } 542 543 return status; 544 } 545 546 /* 547 * Stop subscription. 548 */ 549 PJ_DEF(pj_status_t) pjsip_event_sub_unsubscribe( pjsip_event_sub *sub ) 550 { 551 pjsip_tx_data *tdata; 552 const pjsip_route_hdr *route; 553 pj_status_t status; 554 555 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): unsubscribing...", 556 sub, state[sub->state].ptr)); 557 558 /* Lock subscription. */ 559 pj_mutex_lock(sub->mutex); 560 561 pj_assert(sub->role == PJSIP_ROLE_UAC); 562 563 /* Kill refresh timer, if any. */ 564 if (sub->timer.id != 0) { 565 sub->timer.id = 0; 566 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 567 } 568 569 /* Create request. */ 570 tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 571 &SUBSCRIBE, 572 sub->to->uri, 573 sub->from, sub->to, 574 sub->contact, sub->call_id, 575 sub->cseq++, 576 NULL); 577 578 if (!tdata) { 579 pj_mutex_unlock(sub->mutex); 580 return -1; 581 } 582 583 /* Add headers to request. */ 584 pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); 585 sub->uac_expires->ivalue = 0; 586 pjsip_msg_add_hdr( tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->uac_expires)); 587 588 /* Add authentication. */ 589 pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, 590 sub->cred_cnt, sub->cred_info); 591 592 593 /* Route set. */ 594 route = sub->route_set.next; 595 while (route != &sub->route_set) { 596 pj_list_insert_before( &tdata->msg->hdr, 597 pjsip_hdr_shallow_clone(tdata->pool, route)); 598 route = route->next; 599 } 600 601 /* Prevent timer from refreshing itself. */ 602 sub->default_interval = 0; 603 604 /* Set state. */ 605 sub_set_state( sub, PJSIP_EVENT_SUB_STATE_TERMINATED ); 606 607 /* Send the request. */ 608 status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, 609 &on_subscribe_response); 610 if (status == 0) { 611 sub->pending_tsx++; 612 } 613 614 pj_mutex_unlock(sub->mutex); 615 616 if (status != 0) { 617 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): FAILED to unsubscribe!", 618 sub, state[sub->state].ptr)); 619 } 620 621 return status; 622 } 623 624 /* 625 * Send notify. 626 */ 627 PJ_DEF(pj_status_t) pjsip_event_sub_notify(pjsip_event_sub *sub, 628 pjsip_event_sub_state new_state, 629 const pj_str_t *reason, 630 pjsip_msg_body *body) 631 { 632 pjsip_tx_data *tdata; 633 pjsip_sub_state_hdr *ss_hdr; 634 const pjsip_route_hdr *route; 635 pj_time_val now; 636 pj_status_t status; 637 pjsip_event_sub_state old_state = sub->state; 638 639 pj_gettimeofday(&now); 640 641 pj_assert(sub->role == PJSIP_ROLE_UAS); 642 if (sub->role != PJSIP_ROLE_UAS) 643 return -1; 644 645 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sending NOTIFY", 646 sub, state[new_state].ptr)); 647 648 /* Lock subscription. */ 649 pj_mutex_lock(sub->mutex); 650 651 /* Can not send NOTIFY if current state is NULL. We can accept TERMINATED. */ 652 if (sub->state==PJSIP_EVENT_SUB_STATE_NULL) { 653 pj_assert(0); 654 pj_mutex_unlock(sub->mutex); 655 return -1; 656 } 657 658 /* Update state no matter what. */ 659 sub_set_state(sub, new_state); 660 661 /* Create transmit data. */ 662 tdata = pjsip_endpt_create_request_from_hdr( sub->endpt, 663 &NOTIFY, 664 sub->to->uri, 665 sub->from, sub->to, 666 sub->contact, sub->call_id, 667 sub->cseq++, 668 NULL); 669 if (!tdata) { 670 pj_mutex_unlock(sub->mutex); 671 return -1; 672 } 673 674 /* Add Event header. */ 675 pjsip_msg_add_hdr(tdata->msg, pjsip_hdr_shallow_clone(tdata->pool, sub->event)); 676 677 /* Add Subscription-State header. */ 678 ss_hdr = pjsip_sub_state_hdr_create(tdata->pool); 679 ss_hdr->sub_state = state[new_state]; 680 ss_hdr->expires_param = sub->expiry_time.sec - now.sec; 681 if (ss_hdr->expires_param < 0) 682 ss_hdr->expires_param = 0; 683 if (reason) 684 pj_strdup(tdata->pool, &ss_hdr->reason_param, reason); 685 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)ss_hdr); 686 687 /* Add Allow-Events header. */ 688 pjsip_msg_add_hdr( tdata->msg, 689 pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); 690 691 /* Add authentication */ 692 pjsip_auth_init_req( sub->pool, tdata, &sub->auth_sess, 693 sub->cred_cnt, sub->cred_info); 694 695 /* Route set. */ 696 route = sub->route_set.next; 697 while (route != &sub->route_set) { 698 pj_list_insert_before( &tdata->msg->hdr, 699 pjsip_hdr_shallow_clone(tdata->pool, route)); 700 route = route->next; 701 } 702 703 /* Attach body. */ 704 tdata->msg->body = body; 705 706 /* That's it, send! */ 707 status = pjsip_endpt_send_request( sub->endpt, tdata, -1, sub, &on_notify_response); 708 if (status == 0) 709 sub->pending_tsx++; 710 711 /* If terminated notify application. */ 712 if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { 713 if (sub->cb.on_sub_terminated) { 714 sub->pending_tsx++; 715 (*sub->cb.on_sub_terminated)(sub, reason); 716 sub->pending_tsx--; 717 } 718 } 719 720 /* Unlock subscription. */ 721 pj_mutex_unlock(sub->mutex); 722 723 if (status != 0) { 724 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): failed to send NOTIFY", 725 sub, state[sub->state].ptr)); 726 } 727 728 if (sub->delete_flag && sub->pending_tsx <= 0) { 729 pjsip_event_sub_destroy(sub); 730 } 731 return status; 732 } 733 734 735 /* If this timer callback is called, it means subscriber hasn't refreshed its 736 * subscription on-time. Set the state to terminated. This will also send 737 * NOTIFY with Subscription-State set to terminated. 738 */ 739 static void uas_expire_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) 740 { 741 pjsip_event_sub *sub = entry->user_data; 742 pj_str_t reason = { "timeout", 7 }; 743 744 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): UAS subscription expired!", 745 sub, state[sub->state].ptr)); 746 747 pj_mutex_lock(sub->mutex); 748 sub->timer.id = 0; 749 750 if (sub->cb.on_sub_terminated && sub->state!=PJSIP_EVENT_SUB_STATE_TERMINATED) { 751 /* Notify application, but prevent app from destroying the sub. */ 752 ++sub->pending_tsx; 753 (*sub->cb.on_sub_terminated)(sub, &reason); 754 --sub->pending_tsx; 755 } 756 //pjsip_event_sub_notify( sub, PJSIP_EVENT_SUB_STATE_TERMINATED, 757 // &reason, NULL); 758 pj_mutex_unlock(sub->mutex); 759 760 } 761 762 /* Schedule notifier expiration. */ 763 static void sub_schedule_uas_expire( pjsip_event_sub *sub, int sec_delay) 764 { 765 pj_time_val delay = { 0, 0 }; 766 pj_parsed_time pt; 767 768 if (sub->timer.id != 0) 769 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 770 771 pj_gettimeofday(&sub->expiry_time); 772 sub->expiry_time.sec += sec_delay; 773 774 sub->timer.id = TIMER_ID_UAS_EXPIRY; 775 sub->timer.user_data = sub; 776 sub->timer.cb = &uas_expire_timer_cb; 777 delay.sec = sec_delay; 778 pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay); 779 780 pj_time_decode(&sub->expiry_time, &pt); 781 PJ_LOG(4,(THIS_FILE, 782 "event_sub%p (%s)(UAS): will expire at %02d:%02d:%02d (in %d secs)", 783 sub, state[sub->state].ptr, pt.hour, pt.min, pt.sec, sec_delay)); 784 } 785 786 /* This timer is called for UAC to refresh the subscription. */ 787 static void refresh_timer_cb( pj_timer_heap_t *timer_heap, pj_timer_entry *entry) 788 { 789 pjsip_event_sub *sub = entry->user_data; 790 791 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): refresh subscription timer", 792 sub, state[sub->state].ptr)); 793 794 pj_mutex_lock(sub->mutex); 795 sub->timer.id = 0; 796 send_sub_refresh(sub); 797 pj_mutex_unlock(sub->mutex); 798 } 799 800 801 /* This will update the UAC's refresh schedule. */ 802 static void update_next_refresh(pjsip_event_sub *sub, int interval) 803 { 804 pj_time_val delay = {0, 0}; 805 pj_parsed_time pt; 806 807 if (interval < SECONDS_BEFORE_EXPIRY) { 808 PJ_LOG(4,(THIS_FILE, 809 "event_sub%p (%s): expiration delay too short (%d sec)! updated.", 810 sub, state[sub->state].ptr, interval)); 811 interval = SECONDS_BEFORE_EXPIRY; 812 } 813 814 if (sub->timer.id != 0) 815 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 816 817 sub->timer.id = TIMER_ID_REFRESH; 818 sub->timer.user_data = sub; 819 sub->timer.cb = &refresh_timer_cb; 820 pj_gettimeofday(&sub->expiry_time); 821 delay.sec = interval - SECONDS_BEFORE_EXPIRY; 822 sub->expiry_time.sec += delay.sec; 823 824 pj_time_decode(&sub->expiry_time, &pt); 825 PJ_LOG(4,(THIS_FILE, 826 "event_sub%p (%s): will send SUBSCRIBE at %02d:%02d:%02d (in %d secs)", 827 sub, state[sub->state].ptr, 828 pt.hour, pt.min, pt.sec, 829 delay.sec)); 830 831 pjsip_endpt_schedule_timer( sub->endpt, &sub->timer, &delay ); 832 } 833 834 835 /* Find subscription in the hash table. 836 * If found, lock the subscription before returning to caller. 837 */ 838 static pjsip_event_sub *find_sub(pjsip_rx_data *rdata) 839 { 840 pj_str_t key; 841 pjsip_role_e role; 842 pjsip_event_sub *sub; 843 pjsip_method *method = &rdata->msg->line.req.method; 844 pj_str_t *tag; 845 846 if (rdata->msg->type == PJSIP_REQUEST_MSG) { 847 if (pjsip_method_cmp(method, &SUBSCRIBE)==0) { 848 role = PJSIP_ROLE_UAS; 849 tag = &rdata->to_tag; 850 } else { 851 pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); 852 role = PJSIP_ROLE_UAC; 853 tag = &rdata->to_tag; 854 } 855 } else { 856 if (pjsip_method_cmp(&rdata->cseq->method, &SUBSCRIBE)==0) { 857 role = PJSIP_ROLE_UAC; 858 tag = &rdata->from_tag; 859 } else { 860 pj_assert(pjsip_method_cmp(method, &NOTIFY) == 0); 861 role = PJSIP_ROLE_UAS; 862 tag = &rdata->from_tag; 863 } 864 } 865 create_subscriber_key( &key, rdata->pool, role, &rdata->call_id, tag); 866 867 pj_mutex_lock(mgr.mutex); 868 sub = pj_hash_get(mgr.ht, key.ptr, key.slen); 869 if (sub) 870 pj_mutex_lock(sub->mutex); 871 pj_mutex_unlock(mgr.mutex); 872 873 return sub; 874 } 875 876 877 /* This function is called when we receive SUBSCRIBE request message 878 * to refresh existing subscription. 879 */ 880 static void on_received_sub_refresh( pjsip_event_sub *sub, 881 pjsip_transaction *tsx, pjsip_rx_data *rdata) 882 { 883 pjsip_event_hdr *e; 884 pjsip_expires_hdr *expires; 885 pj_str_t hname; 886 int status = 200; 887 pj_str_t reason_phrase = { NULL, 0 }; 888 int new_state = sub->state; 889 int old_state = sub->state; 890 int new_interval = 0; 891 pjsip_tx_data *tdata; 892 893 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received target refresh", 894 sub, state[sub->state].ptr)); 895 896 /* Check that the event matches. */ 897 hname = pj_str("Event"); 898 e = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); 899 if (!e) { 900 status = 400; 901 reason_phrase = pj_str("Missing Event header"); 902 goto send_response; 903 } 904 if (pj_stricmp(&e->event_type, &sub->event->event_type) != 0 || 905 pj_stricmp(&e->id_param, &sub->event->id_param) != 0) 906 { 907 status = 481; 908 reason_phrase = pj_str("Subscription does not exist"); 909 goto send_response; 910 } 911 912 /* Check server state. */ 913 if (sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED) { 914 status = 481; 915 reason_phrase = pj_str("Subscription does not exist"); 916 goto send_response; 917 } 918 919 /* Check expires header. */ 920 expires = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_EXPIRES, NULL); 921 if (!expires) { 922 /* 923 status = 400; 924 reason_phrase = pj_str("Missing Expires header"); 925 goto send_response; 926 */ 927 new_interval = sub->default_interval; 928 } else { 929 /* Check that interval is not too short. 930 * Note that expires time may be zero (for unsubscription). 1297 1298 *state = PJSIP_EVSUB_STATE_UNKNOWN; 1299 *state_str = &sub_state->sub_state; 1300 1301 } 1302 } 1303 1304 /* 1305 * Transaction event processing by UAC, after subscription is sent. 1306 */ 1307 static void on_tsx_state_uac( pjsip_evsub *sub, pjsip_transaction *tsx, 1308 pjsip_event *event ) 1309 { 1310 1311 if (pjsip_method_cmp(&tsx->method, &sub->method)==0) { 1312 1313 /* Received response to outgoing request that establishes/refresh 1314 * subscription. 931 1315 */ 932 new_interval = expires->ivalue; 933 if (new_interval != 0 && new_interval < SECONDS_BEFORE_EXPIRY) { 934 status = PJSIP_SC_INTERVAL_TOO_BRIEF; 935 goto send_response; 936 } 937 } 938 939 /* Update interval. */ 940 sub->default_interval = new_interval; 941 pj_gettimeofday(&sub->expiry_time); 942 sub->expiry_time.sec += new_interval; 943 944 /* Update timer only if this is not unsubscription. */ 945 if (new_interval > 0) { 946 sub->default_interval = new_interval; 947 sub_schedule_uas_expire( sub, new_interval ); 948 949 /* Call callback. */ 950 if (sub->cb.on_received_refresh) { 951 sub->pending_tsx++; 952 (*sub->cb.on_received_refresh)(sub, rdata); 953 sub->pending_tsx--; 954 } 955 } 956 957 send_response: 958 tdata = pjsip_endpt_create_response( sub->endpt, rdata, status); 959 if (tdata) { 960 if (reason_phrase.slen) 961 tdata->msg->line.status.reason = reason_phrase; 962 963 /* Add Expires header. */ 964 expires = pjsip_expires_hdr_create(tdata->pool); 965 expires->ivalue = sub->default_interval; 966 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)expires); 967 968 if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { 969 pjsip_msg_add_hdr(tdata->msg, 970 pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events)); 971 } 972 /* Send down to transaction. */ 973 pjsip_tsx_on_tx_msg(tsx, tdata); 974 } 975 976 if (sub->default_interval==0 || !PJSIP_IS_STATUS_IN_CLASS(status,200)) { 977 /* Notify application if sub is terminated. */ 978 new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; 979 sub_set_state(sub, new_state); 980 if (new_state!=old_state && sub->cb.on_sub_terminated) { 981 pj_str_t reason = {"", 0}; 982 if (reason_phrase.slen) reason = reason_phrase; 983 else reason = *pjsip_get_status_text(status); 984 985 sub->pending_tsx++; 986 (*sub->cb.on_sub_terminated)(sub, &reason); 987 sub->pending_tsx--; 988 } 989 } 990 991 pj_mutex_unlock(sub->mutex); 992 993 /* Prefer to call log when we're not holding the mutex. */ 994 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): sent refresh response %s, status=%d", 995 sub, state[sub->state].ptr, 996 (tdata ? tdata->obj_name : "null"), status)); 997 998 /* Check if application has requested deletion. */ 999 if (sub->delete_flag && sub->pending_tsx <= 0) { 1000 pjsip_event_sub_destroy(sub); 1001 } 1002 1003 } 1004 1005 1006 /* This function is called when we receive SUBSCRIBE request message for 1007 * a new subscription. 1008 */ 1009 static void on_new_subscription( pjsip_transaction *tsx, pjsip_rx_data *rdata ) 1010 { 1011 package *pkg; 1012 pj_pool_t *pool; 1013 pjsip_event_sub *sub = NULL; 1014 pj_str_t hname; 1015 int status = 200; 1016 pj_str_t reason = { NULL, 0 }; 1017 pjsip_tx_data *tdata; 1018 pjsip_expires_hdr *expires; 1019 pjsip_accept_hdr *accept; 1020 pjsip_event_hdr *evhdr; 1021 1022 /* Get the Event header. */ 1023 hname = pj_str("Event"); 1024 evhdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); 1025 if (!evhdr) { 1026 status = 400; 1027 reason = pj_str("No Event header in request"); 1028 goto send_response; 1029 } 1030 1031 /* Find corresponding package. 1032 * We don't lock the manager's mutex since we assume the package list 1033 * won't change once the application is running! 1034 */ 1035 pkg = mgr.pkg_list.next; 1036 while (pkg != &mgr.pkg_list) { 1037 if (pj_stricmp(&pkg->event, &evhdr->event_type) == 0) 1038 break; 1039 pkg = pkg->next; 1040 } 1041 1042 if (pkg == &mgr.pkg_list) { 1043 /* Event type is not supported by any packages! */ 1044 status = 489; 1045 reason = pj_str("Bad Event"); 1046 goto send_response; 1047 } 1048 1049 /* First check that the Accept specification matches the 1050 * package's Accept types. 1051 */ 1052 accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); 1053 if (accept) { 1054 unsigned i; 1055 pj_str_t *content_type = NULL; 1056 1057 for (i=0; i<accept->count && !content_type; ++i) { 1058 int j; 1059 for (j=0; j<pkg->accept_cnt; ++j) { 1060 if (pj_stricmp(&accept->values[i], &pkg->accept[j])==0) { 1061 content_type = &pkg->accept[j]; 1062 break; 1316 1317 /* First time initial request is sent. */ 1318 if (sub->state == PJSIP_EVSUB_STATE_NULL && 1319 tsx->state == PJSIP_TSX_STATE_CALLING) 1320 { 1321 set_state(sub, PJSIP_EVSUB_STATE_SENT, NULL, event); 1322 return; 1323 } 1324 1325 /* Only interested in final response */ 1326 if (tsx->state != PJSIP_TSX_STATE_COMPLETED && 1327 tsx->state != PJSIP_TSX_STATE_TERMINATED) 1328 { 1329 return; 1330 } 1331 1332 /* Handle authentication. */ 1333 if (tsx->status_code==401 || tsx->status_code==407) { 1334 pjsip_tx_data *tdata; 1335 pj_status_t status; 1336 1337 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { 1338 /* Previously failed transaction has terminated */ 1339 return; 1340 } 1341 1342 status = pjsip_auth_clt_reinit_req(&sub->dlg->auth_sess, 1343 event->body.tsx_state.src.rdata, 1344 tsx->last_tx, &tdata); 1345 if (status == PJ_SUCCESS) 1346 status = pjsip_dlg_send_request(sub->dlg, tdata, NULL); 1347 1348 if (status != PJ_SUCCESS) { 1349 /* Authentication failed! */ 1350 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, 1351 NULL, 1352 event); 1353 return; 1354 } 1355 1356 return; 1357 } 1358 1359 if (tsx->status_code/100 == 2) { 1360 1361 /* Successfull SUBSCRIBE request! 1362 * This could be: 1363 * - response to initial SUBSCRIBE request 1364 * - response to subsequent refresh 1365 * - response to unsubscription 1366 */ 1367 1368 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { 1369 /* Ignore; this transaction has been processed before */ 1370 return; 1371 } 1372 1373 /* Update UAC refresh time, if response contains Expires header, 1374 * only when we're not unsubscribing. 1375 */ 1376 if (sub->expires->ivalue != 0) { 1377 pjsip_msg *msg; 1378 pjsip_expires_hdr *expires; 1379 1380 msg = event->body.tsx_state.src.rdata->msg_info.msg; 1381 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); 1382 if (expires) { 1383 sub->expires->ivalue = expires->ivalue; 1063 1384 } 1064 1385 } 1065 } 1066 1067 if (!content_type) { 1068 status = PJSIP_SC_NOT_ACCEPTABLE_HERE; 1069 goto send_response; 1070 } 1071 } 1072 1073 /* Check whether the package wants to accept the subscription. */ 1074 pj_assert(pkg->cb.on_query_subscribe != NULL); 1075 (*pkg->cb.on_query_subscribe)(rdata, &status); 1076 if (!PJSIP_IS_STATUS_IN_CLASS(status,200)) 1077 goto send_response; 1078 1079 /* Create new subscription record. */ 1080 pool = pjsip_endpt_create_pool(tsx->endpt, "esub", 1081 SUB_POOL_SIZE, SUB_POOL_INC); 1082 if (!pool) { 1083 status = 500; 1084 goto send_response; 1085 } 1086 sub = pj_pool_calloc(pool, 1, sizeof(*sub)); 1087 sub->pool = pool; 1088 sub->mutex = pj_mutex_create(pool, "esub", PJ_MUTEX_RECURSE); 1089 if (!sub->mutex) { 1090 status = 500; 1091 goto send_response; 1092 } 1093 1094 PJ_LOG(4,(THIS_FILE, "event_sub%p: notifier is created.", sub)); 1095 1096 /* Start locking mutex. */ 1097 pj_mutex_lock(sub->mutex); 1098 1099 /* Init UAS subscription */ 1100 sub->endpt = tsx->endpt; 1101 sub->role = PJSIP_ROLE_UAS; 1102 sub->state = PJSIP_EVENT_SUB_STATE_PENDING; 1103 sub->state_str = state[sub->state]; 1104 pj_list_init(&sub->auth_sess); 1105 pj_list_init(&sub->route_set); 1106 sub->from = pjsip_hdr_clone(pool, rdata->to); 1107 pjsip_fromto_set_from(sub->from); 1108 if (sub->from->tag.slen == 0) { 1109 pj_create_unique_string(pool, &sub->from->tag); 1110 rdata->to->tag = sub->from->tag; 1111 } 1112 sub->to = pjsip_hdr_clone(pool, rdata->from); 1113 pjsip_fromto_set_to(sub->to); 1114 sub->contact = pjsip_contact_hdr_create(pool); 1115 sub->contact->uri = sub->from->uri; 1116 sub->call_id = pjsip_cid_hdr_create(pool); 1117 pj_strdup(pool, &sub->call_id->id, &rdata->call_id); 1118 sub->cseq = pj_rand() % 0xFFFF; 1119 1120 expires = pjsip_msg_find_hdr( rdata->msg, PJSIP_H_EXPIRES, NULL); 1121 if (expires) { 1122 sub->default_interval = expires->ivalue; 1123 if (sub->default_interval > 0 && 1124 sub->default_interval < SECONDS_BEFORE_EXPIRY) 1125 { 1126 status = 423; /* Interval too short. */ 1127 goto send_response; 1128 } 1129 } else { 1130 sub->default_interval = 600; 1131 } 1132 1133 /* Clone Event header. */ 1134 sub->event = pjsip_hdr_clone(pool, evhdr); 1135 1136 /* Register to hash table. */ 1137 create_subscriber_key(&sub->key, pool, PJSIP_ROLE_UAS, &sub->call_id->id, 1138 &sub->from->tag); 1139 pj_mutex_lock(mgr.mutex); 1140 pj_hash_set(pool, mgr.ht, sub->key.ptr, sub->key.slen, sub); 1141 pj_mutex_unlock(mgr.mutex); 1142 1143 /* Set timer where subscription will expire only when expires<>0. 1144 * Subscriber may send new subscription with expires==0. 1145 */ 1146 if (sub->default_interval != 0) { 1147 sub_schedule_uas_expire( sub, sub->default_interval-SECONDS_BEFORE_EXPIRY); 1148 } 1149 1150 /* Notify application. */ 1151 if (pkg->cb.on_subscribe) { 1152 pjsip_event_sub_cb *cb = NULL; 1153 sub->pending_tsx++; 1154 (*pkg->cb.on_subscribe)(sub, rdata, &cb, &sub->default_interval); 1155 sub->pending_tsx--; 1156 if (cb == NULL) 1157 pj_memset(&sub->cb, 0, sizeof(*cb)); 1158 else 1159 pj_memcpy(&sub->cb, cb, sizeof(*cb)); 1160 } 1161 1162 1163 send_response: 1164 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s)(UAS): status=%d", 1165 sub, state[sub->state].ptr, status)); 1166 1167 tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status); 1168 if (tdata) { 1169 if (reason.slen) { 1170 /* Customize reason text. */ 1171 tdata->msg->line.status.reason = reason; 1172 } 1173 if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { 1174 /* Add Expires header. */ 1175 pjsip_expires_hdr *hdr; 1176 1177 hdr = pjsip_expires_hdr_create(tdata->pool); 1178 hdr->ivalue = sub->default_interval; 1179 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr ); 1180 } 1181 if (status == 423) { 1182 /* Add Min-Expires header. */ 1183 pjsip_min_expires_hdr *hdr; 1184 1185 hdr = pjsip_min_expires_hdr_create(tdata->pool); 1186 hdr->ivalue = SECONDS_BEFORE_EXPIRY; 1187 pjsip_msg_add_hdr( tdata->msg, (pjsip_hdr*)hdr); 1188 } 1189 if (status == 489 || 1190 status==PJSIP_SC_NOT_ACCEPTABLE_HERE || 1191 PJSIP_IS_STATUS_IN_CLASS(status,200)) 1192 { 1193 /* Add Allow-Events header. */ 1194 pjsip_hdr *hdr; 1195 hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); 1196 pjsip_msg_add_hdr(tdata->msg, hdr); 1197 1198 /* Should add Accept header?. */ 1199 } 1200 1201 pjsip_tsx_on_tx_msg(tsx, tdata); 1202 } 1203 1204 /* If received new subscription with expires=0, terminate. */ 1205 if (sub && sub->default_interval == 0) { 1206 pj_assert(sub->state == PJSIP_EVENT_SUB_STATE_TERMINATED); 1207 if (sub->cb.on_sub_terminated) { 1208 pj_str_t reason = { "timeout", 7 }; 1209 (*sub->cb.on_sub_terminated)(sub, &reason); 1210 } 1211 } 1212 1213 if (!PJSIP_IS_STATUS_IN_CLASS(status,200) || (sub && sub->delete_flag)) { 1214 if (sub && sub->mutex) { 1215 pjsip_event_sub_destroy(sub); 1216 } else if (sub) { 1217 pjsip_endpt_destroy_pool(tsx->endpt, sub->pool); 1218 } 1219 } else { 1220 pj_assert(status >= 200); 1221 pj_mutex_unlock(sub->mutex); 1222 } 1223 } 1224 1225 /* This is the main callback when SUBSCRIBE request is received. */ 1226 static void on_subscribe_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) 1227 { 1228 pjsip_event_sub *sub = find_sub(rdata); 1229 1230 if (sub) 1231 on_received_sub_refresh(sub, tsx, rdata); 1232 else 1233 on_new_subscription(tsx, rdata); 1234 } 1235 1236 1237 /* This callback is called when response to SUBSCRIBE is received. */ 1238 static void on_subscribe_response(void *token, pjsip_event *event) 1239 { 1240 pjsip_event_sub *sub = token; 1241 pjsip_transaction *tsx = event->obj.tsx; 1242 int new_state, old_state = sub->state; 1243 1244 pj_assert(tsx->status_code >= 200); 1245 if (tsx->status_code < 200) 1246 return; 1247 1248 pj_assert(sub->role == PJSIP_ROLE_UAC); 1249 1250 /* Lock mutex. */ 1251 pj_mutex_lock(sub->mutex); 1252 1253 /* If request failed with 401/407 error, silently retry the request. */ 1254 if (tsx->status_code==401 || tsx->status_code==407) { 1255 pjsip_tx_data *tdata; 1256 tdata = pjsip_auth_reinit_req(sub->endpt, 1257 sub->pool, &sub->auth_sess, 1258 sub->cred_cnt, sub->cred_info, 1259 tsx->last_tx, event->src.rdata ); 1260 if (tdata) { 1261 int status; 1262 pjsip_cseq_hdr *cseq; 1263 cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); 1264 cseq->cseq = sub->cseq++; 1265 status = pjsip_endpt_send_request( sub->endpt, tdata, 1266 -1, sub, 1267 &on_subscribe_response); 1268 if (status == 0) { 1269 pj_mutex_unlock(sub->mutex); 1386 1387 /* Update time */ 1388 update_expires(sub, sub->expires->ivalue); 1389 1390 /* Start UAC refresh timer, only when we're not unsubscribing */ 1391 if (sub->expires->ivalue != 0) { 1392 unsigned timeout = (sub->expires->ivalue > TIME_UAC_REFRESH) ? 1393 sub->expires->ivalue - TIME_UAC_REFRESH : sub->expires->ivalue; 1394 1395 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", 1396 timeout)); 1397 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout); 1398 1399 } else { 1400 /* Otherwise set timer to terminate client subscription when 1401 * NOTIFY to end subscription is not received. 1402 */ 1403 set_timer(sub, TIMER_TYPE_UAC_TERMINATE, TIME_UAC_TERMINATE); 1404 } 1405 1406 /* Set state, if necessary */ 1407 pj_assert(sub->state != PJSIP_EVSUB_STATE_NULL); 1408 if (sub->state == PJSIP_EVSUB_STATE_SENT) { 1409 set_state(sub, PJSIP_EVSUB_STATE_ACCEPTED, NULL, event); 1410 } 1411 1412 } else { 1413 1414 /* Failed SUBSCRIBE request! 1415 * 1416 * The RFC 3265 says that if outgoing SUBSCRIBE fails with status 1417 * other than 481, the subscription is still considered valid for 1418 * the duration of the last Expires. 1419 * 1420 * Since we send refresh about 5 seconds (TIME_UAC_REFRESH) before 1421 * expiration, theoritically the expiration is still valid for the 1422 * next 5 seconds even when we receive non-481 failed response. 1423 * 1424 * Ah, what the heck! 1425 * 1426 * Just terminate now! 1427 * 1428 */ 1429 1430 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED) { 1431 /* Ignore, has been handled before */ 1270 1432 return; 1271 1433 } 1272 } 1273 } 1274 1275 if (PJSIP_IS_STATUS_IN_CLASS(tsx->status_code,200)) { 1276 /* Update To tag. */ 1277 if (sub->to->tag.slen == 0) 1278 pj_strdup(sub->pool, &sub->to->tag, &event->src.rdata->to_tag); 1279 1280 new_state = sub->state; 1281 1282 } else if (tsx->status_code == 481) { 1283 new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; 1284 1285 } else if (tsx->status_code >= 300) { 1286 /* RFC 3265 Section 3.1.4.2: 1287 * If a SUBSCRIBE request to refresh a subscription fails 1288 * with a non-481 response, the original subscription is still 1289 * considered valid for the duration of original exires. 1434 1435 /* Kill any timer. */ 1436 set_timer(sub, TIMER_TYPE_NONE, 0); 1437 1438 /* Set state to TERMINATED */ 1439 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, 1440 NULL, event); 1441 1442 } 1443 1444 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method) == 0) { 1445 1446 /* Incoming NOTIFY. 1447 * This can be the result of: 1448 * - Initial subscription response 1449 * - UAS updating the resource info. 1450 * - Unsubscription response. 1451 */ 1452 int st_code = 200; 1453 pj_str_t *st_text = NULL; 1454 pjsip_hdr res_hdr; 1455 pjsip_msg_body *body = NULL; 1456 1457 pjsip_rx_data *rdata; 1458 pjsip_msg *msg; 1459 pjsip_sub_state_hdr *sub_state; 1460 1461 pjsip_evsub_state new_state; 1462 pj_str_t *new_state_str; 1463 1464 pjsip_tx_data *tdata; 1465 pj_status_t status; 1466 int next_refresh; 1467 1468 /* Only want to handle initial NOTIFY receive event. */ 1469 if (tsx->state != PJSIP_TSX_STATE_TRYING) 1470 return; 1471 1472 1473 rdata = event->body.tsx_state.src.rdata; 1474 msg = rdata->msg_info.msg; 1475 1476 pj_list_init(&res_hdr); 1477 1478 /* Get subscription state header. */ 1479 sub_state = pjsip_msg_find_hdr_by_name(msg, &STR_SUB_STATE, NULL); 1480 if (sub_state == NULL) { 1481 1482 pjsip_warning_hdr *warn_hdr; 1483 pj_str_t warn_text = { "Missing Subscription-State header", 33}; 1484 1485 /* Bad request! Add warning header. */ 1486 st_code = PJSIP_SC_BAD_REQUEST; 1487 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, 1488 pjsip_endpt_name(sub->endpt), 1489 &warn_text); 1490 pj_list_push_back(&res_hdr, warn_hdr); 1491 } 1492 1493 /* Call application registered callback to handle incoming NOTIFY, 1494 * if any. 1495 */ 1496 if (st_code==200 && sub->user.on_rx_notify) { 1497 (*sub->user.on_rx_notify)(sub, rdata, &st_code, &st_text, 1498 &res_hdr, &body); 1499 1500 /* Application MUST specify final response! */ 1501 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; }); 1502 1503 /* Must be a valid status code */ 1504 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; }); 1505 } 1506 1507 1508 /* If non-2xx should be returned, then send the response. 1509 * No need to update server subscription state. 1510 */ 1511 if (st_code >= 300) { 1512 status = create_response(sub, rdata, st_code, st_text, &res_hdr, 1513 body, &tdata); 1514 if (status == PJ_SUCCESS) { 1515 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); 1516 } 1517 1518 /* Start timer to terminate subscription, just in case server 1519 * is not able to generate NOTIFY to our response. 1520 */ 1521 if (status == PJ_SUCCESS) { 1522 unsigned timeout = TIME_UAC_WAIT_NOTIFY; 1523 set_timer(sub, TIMER_TYPE_UAC_WAIT_NOTIFY, timeout); 1524 } else { 1525 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL); 1526 } 1527 1528 return; 1529 } 1530 1531 /* Update expiration from the value of expires param in 1532 * Subscription-State header, but ONLY when subscription state 1533 * is "active" or "pending", AND the header contains expires param. 1534 */ 1535 if (sub->expires->ivalue != 0 && 1536 sub_state->expires_param >= 0 && 1537 (pj_stricmp(&sub_state->sub_state, &STR_ACTIVE)==0 || 1538 pj_stricmp(&sub_state->sub_state, &STR_PENDING)==0)) 1539 { 1540 next_refresh = sub_state->expires_param; 1541 1542 } else { 1543 next_refresh = sub->expires->ivalue; 1544 } 1545 1546 /* Update time */ 1547 update_expires(sub, next_refresh); 1548 1549 /* Start UAC refresh timer, only when we're not unsubscribing */ 1550 if (sub->expires->ivalue != 0) { 1551 unsigned timeout = (next_refresh > TIME_UAC_REFRESH) ? 1552 next_refresh - TIME_UAC_REFRESH : next_refresh; 1553 1554 PJ_LOG(5,(sub->obj_name, "Will refresh in %d seconds", timeout)); 1555 set_timer(sub, TIMER_TYPE_UAC_REFRESH, timeout); 1556 } 1557 1558 /* Find out the state */ 1559 get_hdr_state(sub_state, &new_state, &new_state_str); 1560 1561 /* Send response. */ 1562 status = create_response(sub, rdata, st_code, st_text, &res_hdr, 1563 body, &tdata); 1564 if (status == PJ_SUCCESS) 1565 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); 1566 1567 /* Set the state */ 1568 if (status == PJ_SUCCESS) { 1569 set_state(sub, new_state, new_state_str, event); 1570 } else { 1571 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, event); 1572 } 1573 1574 1575 } else { 1576 1577 /* 1578 * Unexpected method! 1579 */ 1580 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s", 1581 (int)tsx->method.name.slen, tsx->method.name.ptr)); 1582 } 1583 } 1584 1585 1586 /* 1587 * Transaction event processing by UAS, after subscription is accepted. 1588 */ 1589 static void on_tsx_state_uas( pjsip_evsub *sub, pjsip_transaction *tsx, 1590 pjsip_event *event) 1591 { 1592 1593 if (pjsip_method_cmp(&tsx->method, &sub->method) == 0) { 1594 1595 /* 1596 * Incoming request (e.g. SUBSCRIBE or REFER) to refresh subsciption. 1290 1597 * 1291 * Note:1292 * Since we normally send SUBSCRIBE for refreshing the subscription,1293 * it means the subscription already expired anyway. So we terminate1294 * the subscription now.1295 1598 */ 1296 if (sub->state != PJSIP_EVENT_SUB_STATE_ACTIVE) { 1297 new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; 1298 } else { 1299 /* Use this to be compliant with Section 3.1.4.2 1300 new_state = sub->state; 1301 */ 1302 new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; 1303 } 1599 pjsip_rx_data *rdata; 1600 pjsip_event_hdr *event_hdr; 1601 pjsip_expires_hdr *expires; 1602 pjsip_msg *msg; 1603 pjsip_tx_data *tdata; 1604 int st_code = 200; 1605 pj_str_t *st_text = NULL; 1606 pjsip_hdr res_hdr; 1607 pjsip_msg_body *body = NULL; 1608 pjsip_evsub_state old_state; 1609 pj_str_t old_state_str; 1610 pj_status_t status; 1611 1612 1613 /* Only wants to handle the first event when the request is 1614 * received. 1615 */ 1616 if (tsx->state != PJSIP_TSX_STATE_TRYING) 1617 return; 1618 1619 rdata = event->body.tsx_state.src.rdata; 1620 msg = rdata->msg_info.msg; 1621 1622 /* Set expiration time based on client request (in Expires header), 1623 * or package default expiration time. 1624 */ 1625 event_hdr = pjsip_msg_find_hdr_by_name(msg, &STR_EVENT, NULL); 1626 expires = pjsip_msg_find_hdr(msg, PJSIP_H_EXPIRES, NULL); 1627 if (event_hdr && expires) { 1628 struct evpkg *evpkg; 1629 1630 evpkg = find_pkg(&event_hdr->event_type); 1631 if (evpkg) { 1632 if (expires->ivalue < (pj_int32_t)evpkg->pkg_expires) 1633 sub->expires->ivalue = expires->ivalue; 1634 else 1635 sub->expires->ivalue = evpkg->pkg_expires; 1636 } 1637 } 1638 1639 /* Update time (before calling on_rx_refresh, since application 1640 * will send NOTIFY. 1641 */ 1642 update_expires(sub, sub->expires->ivalue); 1643 1644 1645 /* Save old state. 1646 * If application respond with non-2xx, revert to old state. 1647 */ 1648 old_state = sub->state; 1649 old_state_str = sub->state_str; 1650 1651 if (sub->expires->ivalue == 0) { 1652 sub->state = PJSIP_EVSUB_STATE_TERMINATED; 1653 sub->state_str = evsub_state_names[sub->state]; 1654 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) { 1655 sub->state = PJSIP_EVSUB_STATE_ACCEPTED; 1656 sub->state_str = evsub_state_names[sub->state]; 1657 } 1658 1659 /* Call application's on_rx_refresh, just in case it wants to send 1660 * response other than 200 (OK) 1661 */ 1662 pj_list_init(&res_hdr); 1663 1664 (*sub->user.on_rx_refresh)(sub, rdata, &st_code, &st_text, 1665 &res_hdr, &body); 1666 1667 /* Application MUST specify final response! */ 1668 PJ_ASSERT_ON_FAIL(st_code >= 200, {st_code=200; }); 1669 1670 /* Must be a valid status code */ 1671 PJ_ASSERT_ON_FAIL(st_code <= 699, {st_code=500; }); 1672 1673 1674 /* Create and send response */ 1675 status = create_response(sub, rdata, st_code, st_text, &res_hdr, 1676 body, &tdata); 1677 if (status == PJ_SUCCESS) { 1678 /* Add expires header: */ 1679 pjsip_msg_add_hdr( tdata->msg, 1680 pjsip_hdr_shallow_clone(tdata->pool, 1681 sub->expires)); 1682 1683 /* Send */ 1684 status = pjsip_dlg_send_response(sub->dlg, tsx, tdata); 1685 } 1686 1687 /* Update state or revert state */ 1688 if (st_code/100==2) { 1689 1690 if (sub->expires->ivalue == 0) { 1691 set_state(sub, sub->state, NULL, event); 1692 } else if (sub->state == PJSIP_EVSUB_STATE_NULL) { 1693 set_state(sub, sub->state, NULL, event); 1694 } 1695 1696 /* Set UAS timeout timer, when state is not terminated. */ 1697 if (sub->state != PJSIP_EVSUB_STATE_TERMINATED) { 1698 PJ_LOG(5,(sub->obj_name, "UAS timeout in %d seconds", 1699 sub->expires->ivalue)); 1700 set_timer(sub, TIMER_TYPE_UAS_TIMEOUT, 1701 sub->expires->ivalue); 1702 } 1703 1704 } else { 1705 sub->state = old_state; 1706 sub->state_str = old_state_str; 1707 } 1708 1709 1710 } else if (pjsip_method_cmp(&tsx->method, &pjsip_notify_method)==0) { 1711 1712 /* Handle authentication */ 1713 if (tsx->state == PJSIP_TSX_STATE_COMPLETED && 1714 (tsx->status_code==401 || tsx->status_code==407)) 1715 { 1716 pjsip_rx_data *rdata = event->body.tsx_state.src.rdata; 1717 pjsip_tx_data *tdata; 1718 pj_status_t status; 1719 1720 status = pjsip_auth_clt_reinit_req( &sub->dlg->auth_sess, rdata, 1721 tsx->last_tx, &tdata); 1722 if (status == PJ_SUCCESS) 1723 status = pjsip_dlg_send_request( sub->dlg, tdata, NULL ); 1724 1725 if (status != PJ_SUCCESS) { 1726 /* Can't authenticate. Terminate session (?) */ 1727 set_state(sub, PJSIP_EVSUB_STATE_TERMINATED, NULL, NULL); 1728 } 1729 } 1730 1304 1731 } else { 1305 pj_assert(0); 1306 new_state = sub->state; 1307 } 1308 1309 if (new_state != sub->state && sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) { 1310 sub_set_state(sub, new_state); 1311 } 1312 1313 if (sub->state == PJSIP_EVENT_SUB_STATE_ACTIVE || 1314 sub->state == PJSIP_EVENT_SUB_STATE_PENDING) 1315 { 1732 1316 1733 /* 1317 * Register timer for next subscription refresh, but only when 1318 * we're not unsubscribing. Also update default_interval and Expires 1319 * header. 1734 * Unexpected method! 1320 1735 */ 1321 if (sub->default_interval > 0 && !sub->delete_flag) { 1322 pjsip_expires_hdr *exp = NULL; 1323 1324 /* Could be transaction timeout. */ 1325 if (event->src_type == PJSIP_EVENT_RX_MSG) { 1326 exp = pjsip_msg_find_hdr(event->src.rdata->msg, 1327 PJSIP_H_EXPIRES, NULL); 1328 } 1329 1330 if (exp) { 1331 int delay = exp->ivalue; 1332 if (delay > 0) { 1333 pj_time_val new_expiry; 1334 pj_gettimeofday(&new_expiry); 1335 new_expiry.sec += delay; 1336 if (sub->timer.id==0 || 1337 new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 1338 { 1339 //if (delay > 0 && delay < sub->default_interval) { 1340 sub->default_interval = delay; 1341 sub->uac_expires->ivalue = delay; 1342 update_next_refresh(sub, delay); 1343 } 1344 } 1345 } 1346 } 1347 } 1348 1349 /* Call callback. */ 1350 if (!sub->delete_flag) { 1351 if (sub->cb.on_received_sub_response) { 1352 (*sub->cb.on_received_sub_response)(sub, event); 1353 } 1354 } 1355 1356 /* Notify application if we're terminated. */ 1357 if (new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { 1358 if (sub->cb.on_sub_terminated) { 1359 pj_str_t reason; 1360 if (event->src_type == PJSIP_EVENT_RX_MSG) 1361 reason = event->src.rdata->msg->line.status.reason; 1362 else 1363 reason = *pjsip_get_status_text(tsx->status_code); 1364 1365 (*sub->cb.on_sub_terminated)(sub, &reason); 1366 } 1367 } 1368 1369 /* Decrement pending tsx count. */ 1370 --sub->pending_tsx; 1371 pj_assert(sub->pending_tsx >= 0); 1372 1373 if (sub->delete_flag && sub->pending_tsx <= 0) { 1374 pjsip_event_sub_destroy(sub); 1736 PJ_LOG(4,(sub->obj_name, "Unexpected transaction method %.*s", 1737 (int)tsx->method.name.slen, tsx->method.name.ptr)); 1738 1739 } 1740 } 1741 1742 1743 /* 1744 * Notification when transaction state has changed! 1745 */ 1746 static void mod_evsub_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event) 1747 { 1748 pjsip_evsub *sub = pjsip_tsx_get_evsub(tsx); 1749 1750 if (sub == NULL) { 1751 sub = on_new_transaction(tsx, event); 1752 if (sub == NULL) 1753 return; 1754 } 1755 1756 1757 /* Call on_tsx_state callback, if any. */ 1758 if (sub->user.on_tsx_state) 1759 (*sub->user.on_tsx_state)(sub, tsx, event); 1760 1761 1762 /* Process the event: */ 1763 1764 if (sub->role == PJSIP_ROLE_UAC) { 1765 on_tsx_state_uac(sub, tsx, event); 1375 1766 } else { 1376 pj_mutex_unlock(sub->mutex); 1377 } 1378 1379 /* DO NOT ACCESS sub FROM NOW ON! IT MIGHT HAVE BEEN DELETED */ 1380 } 1381 1382 /* 1383 * This callback called when we receive incoming NOTIFY request. 1384 */ 1385 static void on_notify_request(pjsip_transaction *tsx, pjsip_rx_data *rdata) 1386 { 1387 pjsip_event_sub *sub; 1388 pjsip_tx_data *tdata; 1389 int status = 200; 1390 int old_state; 1391 pj_str_t reason = { NULL, 0 }; 1392 pj_str_t reason_phrase = { NULL, 0 }; 1393 int new_state = PJSIP_EVENT_SUB_STATE_NULL; 1394 1395 /* Find subscription based on Call-ID and From tag. 1396 * This will also automatically lock the subscription, if it's found. 1397 */ 1398 sub = find_sub(rdata); 1399 if (!sub) { 1400 /* RFC 3265: Section 3.2 Description of NOTIFY Behavior: 1401 * Answer with 481 Subscription does not exist. 1402 */ 1403 PJ_LOG(4,(THIS_FILE, "Unable to find subscription for incoming NOTIFY!")); 1404 status = 481; 1405 reason_phrase = pj_str("Subscription does not exist"); 1406 1407 } else { 1408 pj_assert(sub->role == PJSIP_ROLE_UAC); 1409 PJ_LOG(4,(THIS_FILE, "event_sub%p (%s): received NOTIFY", 1410 sub, state[sub->state].ptr)); 1411 1412 } 1413 1414 new_state = old_state = sub->state; 1415 1416 /* RFC 3265: Section 3.2.1 1417 * Check that the Event header match the subscription. 1418 */ 1419 if (status == 200) { 1420 pjsip_event_hdr *hdr; 1421 pj_str_t hname = { "Event", 5 }; 1422 1423 hdr = pjsip_msg_find_hdr_by_name(rdata->msg, &hname, NULL); 1424 if (!hdr) { 1425 status = PJSIP_SC_BAD_REQUEST; 1426 reason_phrase = pj_str("No Event header found"); 1427 } else if (pj_stricmp(&hdr->event_type, &sub->event->event_type) != 0 || 1428 pj_stricmp(&hdr->id_param, &sub->event->id_param) != 0) 1767 on_tsx_state_uas(sub, tsx, event); 1768 } 1769 1770 1771 /* Check transaction TERMINATE event */ 1772 if (tsx->state == PJSIP_TSX_STATE_TERMINATED) { 1773 1774 --sub->pending_tsx; 1775 1776 if (sub->state == PJSIP_EVSUB_STATE_TERMINATED && 1777 sub->pending_tsx == 0) 1429 1778 { 1430 status = 481; 1431 reason_phrase = pj_str("Subscription does not exist"); 1432 } 1433 } 1434 1435 /* Update subscription state and timer. */ 1436 if (status == 200) { 1437 pjsip_sub_state_hdr *hdr; 1438 const pj_str_t hname = { "Subscription-State", 18 }; 1439 const pj_str_t state_active = { "active", 6 }, 1440 state_pending = { "pending", 7}, 1441 state_terminated = { "terminated", 10 }; 1442 1443 hdr = pjsip_msg_find_hdr_by_name( rdata->msg, &hname, NULL); 1444 if (!hdr) { 1445 status = PJSIP_SC_BAD_REQUEST; 1446 reason_phrase = pj_str("No Subscription-State header found"); 1447 goto process; 1448 } 1449 1450 /* 1451 * Update subscription state. 1452 */ 1453 if (pj_stricmp(&hdr->sub_state, &state_active) == 0) { 1454 if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 1455 new_state = PJSIP_EVENT_SUB_STATE_ACTIVE; 1456 } else if (pj_stricmp(&hdr->sub_state, &state_pending) == 0) { 1457 if (sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 1458 new_state = PJSIP_EVENT_SUB_STATE_PENDING; 1459 } else if (pj_stricmp(&hdr->sub_state, &state_terminated) == 0) { 1460 new_state = PJSIP_EVENT_SUB_STATE_TERMINATED; 1461 } else { 1462 new_state = PJSIP_EVENT_SUB_STATE_UNKNOWN; 1463 } 1464 1465 reason = hdr->reason_param; 1466 1467 if (new_state != sub->state && new_state != PJSIP_EVENT_SUB_STATE_NULL && 1468 sub->state != PJSIP_EVENT_SUB_STATE_TERMINATED) 1469 { 1470 sub_set_state(sub, new_state); 1471 if (new_state == PJSIP_EVENT_SUB_STATE_UNKNOWN) { 1472 pj_strdup_with_null(sub->pool, &sub->state_str, &hdr->sub_state); 1473 } else { 1474 sub->state_str = state[new_state]; 1475 } 1476 } 1477 1478 /* 1479 * Update timeout timer in required, just in case notifier changed the 1480 * expiration to shorter time. 1481 * Section 3.2.2: the expires param can only shorten the interval. 1482 */ 1483 if ((sub->state==PJSIP_EVENT_SUB_STATE_ACTIVE || 1484 sub->state==PJSIP_EVENT_SUB_STATE_PENDING) && hdr->expires_param > 0) 1485 { 1486 pj_time_val now, new_expiry; 1487 1488 pj_gettimeofday(&now); 1489 new_expiry.sec = now.sec + hdr->expires_param; 1490 if (sub->timer.id==0 || 1491 new_expiry.sec < sub->expiry_time.sec-SECONDS_BEFORE_EXPIRY/2) 1492 { 1493 update_next_refresh(sub, hdr->expires_param); 1494 } 1495 } 1496 } 1497 1498 process: 1499 /* Note: here we sub MAY BE NULL! */ 1500 1501 /* Send response to NOTIFY */ 1502 tdata = pjsip_endpt_create_response( tsx->endpt, rdata, status ); 1503 if (tdata) { 1504 if (reason_phrase.slen) 1505 tdata->msg->line.status.reason = reason_phrase; 1506 1507 if (PJSIP_IS_STATUS_IN_CLASS(status,200)) { 1508 pjsip_hdr *hdr; 1509 hdr = pjsip_hdr_shallow_clone(tdata->pool, mgr.allow_events); 1510 pjsip_msg_add_hdr( tdata->msg, hdr); 1511 } 1512 1513 pjsip_tsx_on_tx_msg(tsx, tdata); 1514 } 1515 1516 /* Call NOTIFY callback, if any. */ 1517 if (sub && PJSIP_IS_STATUS_IN_CLASS(status,200) && sub->cb.on_received_notify) { 1518 sub->pending_tsx++; 1519 (*sub->cb.on_received_notify)(sub, rdata); 1520 sub->pending_tsx--; 1521 } 1522 1523 /* Check if subscription is terminated and call callback. */ 1524 if (sub && new_state!=old_state && new_state==PJSIP_EVENT_SUB_STATE_TERMINATED) { 1525 if (sub->cb.on_sub_terminated) { 1526 sub->pending_tsx++; 1527 (*sub->cb.on_sub_terminated)(sub, &reason); 1528 sub->pending_tsx--; 1529 } 1530 } 1531 1532 /* Check if application has requested deletion. */ 1533 if (sub && sub->delete_flag && sub->pending_tsx <= 0) { 1534 pjsip_event_sub_destroy(sub); 1535 } else if (sub) { 1536 pj_mutex_unlock(sub->mutex); 1537 } 1538 } 1539 1540 /* This callback is called when we received NOTIFY response. */ 1541 static void on_notify_response(void *token, pjsip_event *event) 1542 { 1543 pjsip_event_sub *sub = token; 1544 pjsip_event_sub_state old_state = sub->state; 1545 pjsip_transaction *tsx = event->obj.tsx; 1546 1547 /* Lock the subscription. */ 1548 pj_mutex_lock(sub->mutex); 1549 1550 pj_assert(sub->role == PJSIP_ROLE_UAS); 1551 1552 /* If request failed with authorization failure, silently retry. */ 1553 if (tsx->status_code==401 || tsx->status_code==407) { 1554 pjsip_tx_data *tdata; 1555 tdata = pjsip_auth_reinit_req(sub->endpt, 1556 sub->pool, &sub->auth_sess, 1557 sub->cred_cnt, sub->cred_info, 1558 tsx->last_tx, event->src.rdata ); 1559 if (tdata) { 1560 int status; 1561 pjsip_cseq_hdr *cseq; 1562 cseq = pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); 1563 cseq->cseq = sub->cseq++; 1564 status = pjsip_endpt_send_request( sub->endpt, tdata, 1565 -1, sub, 1566 &on_notify_response); 1567 if (status == 0) { 1568 pj_mutex_unlock(sub->mutex); 1569 return; 1570 } 1571 } 1572 } 1573 1574 /* Notify application. */ 1575 if (sub->cb.on_received_notify_response) 1576 (*sub->cb.on_received_notify_response)(sub, event); 1577 1578 /* Check for response 481. */ 1579 if (event->obj.tsx->status_code == 481) { 1580 /* Remote says that the subscription does not exist! 1581 * Terminate subscription! 1582 */ 1583 sub_set_state(sub, PJSIP_EVENT_SUB_STATE_TERMINATED); 1584 if (sub->timer.id) { 1585 pjsip_endpt_cancel_timer(sub->endpt, &sub->timer); 1586 sub->timer.id = 0; 1587 } 1588 1589 PJ_LOG(4, (THIS_FILE, 1590 "event_sub%p (%s): got 481 response to NOTIFY. Terminating...", 1591 sub, state[sub->state].ptr)); 1592 1593 /* Notify app. */ 1594 if (sub->state!=old_state && sub->cb.on_sub_terminated) 1595 (*sub->cb.on_sub_terminated)(sub, &event->src.rdata->msg->line.status.reason); 1596 } 1597 1598 /* Decrement pending transaction count. */ 1599 --sub->pending_tsx; 1600 pj_assert(sub->pending_tsx >= 0); 1601 1602 /* Check that the subscription is marked for deletion. */ 1603 if (sub->delete_flag && sub->pending_tsx <= 0) { 1604 pjsip_event_sub_destroy(sub); 1605 } else { 1606 pj_mutex_unlock(sub->mutex); 1607 } 1608 1609 /* DO NOT ACCESS sub, IT MIGHT HAVE BEEN DESTROYED! */ 1610 } 1611 1612 1613 /* This is the transaction handler for incoming SUBSCRIBE and NOTIFY 1614 * requests. 1615 */ 1616 static void tsx_handler( struct pjsip_module *mod, pjsip_event *event ) 1617 { 1618 pjsip_msg *msg; 1619 pjsip_rx_data *rdata; 1620 1621 /* Only want incoming message events. */ 1622 if (event->src_type != PJSIP_EVENT_RX_MSG) 1623 return; 1624 1625 rdata = event->src.rdata; 1626 msg = rdata->msg; 1627 1628 /* Only want to process request messages. */ 1629 if (msg->type != PJSIP_REQUEST_MSG) 1630 return; 1631 1632 /* Only want the first notification. */ 1633 if (event->obj.tsx && event->obj.tsx->status_code >= 100) 1634 return; 1635 1636 if (pjsip_method_cmp(&msg->line.req.method, &SUBSCRIBE)==0) { 1637 /* Process incoming SUBSCRIBE request. */ 1638 on_subscribe_request( event->obj.tsx, rdata ); 1639 } else if (pjsip_method_cmp(&msg->line.req.method, &NOTIFY)==0) { 1640 /* Process incoming NOTIFY request. */ 1641 on_notify_request( event->obj.tsx, rdata ); 1642 } 1643 } 1644 1779 evsub_destroy(sub); 1780 } 1781 1782 } 1783 } 1784 1785 -
pjproject/trunk/pjsip/src/pjsip-simple/evsub_msg.c
- Property svn:keywords set to id
r187 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip _simple/event_notify_msg.h>20 #include <pjsip/print .h>19 #include <pjsip-simple/evsub_msg.h> 20 #include <pjsip/print_util.h> 21 21 #include <pjsip/sip_parser.h> 22 22 #include <pj/pool.h> … … 24 24 #include <pj/except.h> 25 25 26 /* 27 * Event header. 28 */ 26 29 static int pjsip_event_hdr_print( pjsip_event_hdr *hdr, 27 30 char *buf, pj_size_t size); … … 41 44 PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) 42 45 { 43 pj_str_t event = { "Event", 5 }; 44 pjsip_event_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); 46 pjsip_event_hdr *hdr = pj_pool_zalloc(pool, sizeof(*hdr)); 45 47 hdr->type = PJSIP_H_OTHER; 46 hdr->name = hdr->sname = event; 48 hdr->name.ptr = "Event"; 49 hdr->name.slen = 5; 50 hdr->sname.ptr = "o"; 51 hdr->sname.slen = 1; 47 52 hdr->vptr = &event_hdr_vptr; 48 53 pj_list_init(hdr); 54 pj_list_init(&hdr->other_param); 49 55 return hdr; 50 56 } … … 63 69 copy_advance(p, hdr->event_type); 64 70 copy_advance_pair(p, ";id=", 4, hdr->id_param); 65 if (hdr->other_param.slen) 66 copy_advance(p, hdr->other_param); 71 72 printed = pjsip_param_print_on(&hdr->other_param, p, endbuf-p, 73 &pjsip_PARAM_CHAR_SPEC, 74 &pjsip_PARAM_CHAR_SPEC, ';'); 75 if (printed < 0) 76 return printed; 77 78 p += printed; 67 79 return p - buf; 68 80 } … … 74 86 pj_strdup(pool, &hdr->event_type, &rhs->event_type); 75 87 pj_strdup(pool, &hdr->id_param, &rhs->id_param); 76 pj_strdup(pool, &hdr->other_param, &rhs->other_param); 77 return hdr; 78 } 79 80 static pjsip_event_hdr* pjsip_event_hdr_shallow_clone( pj_pool_t *pool, 81 const pjsip_event_hdr *rhs ) 88 pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); 89 return hdr; 90 } 91 92 static pjsip_event_hdr* 93 pjsip_event_hdr_shallow_clone( pj_pool_t *pool, 94 const pjsip_event_hdr *rhs ) 82 95 { 83 96 pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); 84 97 pj_memcpy(hdr, rhs, sizeof(*hdr)); 85 return hdr; 86 } 87 88 89 static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, 90 char *buf, pj_size_t size); 91 static pjsip_allow_events_hdr* 92 pjsip_allow_events_hdr_clone(pj_pool_t *pool, 93 const pjsip_allow_events_hdr *hdr); 94 static pjsip_allow_events_hdr* 95 pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, 96 const pjsip_allow_events_hdr*); 97 98 static pjsip_hdr_vptr allow_event_hdr_vptr = 99 { 100 (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_clone, 101 (pjsip_hdr_clone_fptr) &pjsip_allow_events_hdr_shallow_clone, 102 (pjsip_hdr_print_fptr) &pjsip_allow_events_hdr_print, 103 }; 104 105 98 pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); 99 return hdr; 100 } 101 102 103 /* 104 * Allow-Events header. 105 */ 106 106 PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) 107 107 { 108 pj_str_t allow_events = { "Allow-Events", 12 }; 109 pjsip_allow_events_hdr *hdr = pj_pool_calloc(pool, 1, sizeof(*hdr)); 110 hdr->type = PJSIP_H_OTHER; 111 hdr->name = hdr->sname = allow_events; 112 hdr->vptr = &allow_event_hdr_vptr; 113 pj_list_init(hdr); 114 return hdr; 115 } 116 117 static int pjsip_allow_events_hdr_print(pjsip_allow_events_hdr *hdr, 118 char *buf, pj_size_t size) 119 { 120 char *p = buf; 121 char *endbuf = buf+size; 122 int printed; 123 124 copy_advance(p, hdr->name); 125 *p++ = ':'; 126 *p++ = ' '; 127 128 if (hdr->event_cnt > 0) { 129 int i; 130 copy_advance(p, hdr->events[0]); 131 for (i=1; i<hdr->event_cnt; ++i) { 132 copy_advance_pair(p, ",", 1, hdr->events[i]); 133 } 134 } 135 136 return p - buf; 137 } 138 139 static pjsip_allow_events_hdr* 140 pjsip_allow_events_hdr_clone(pj_pool_t *pool, 141 const pjsip_allow_events_hdr *rhs) 142 { 143 int i; 144 145 pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); 146 hdr->event_cnt = rhs->event_cnt; 147 for (i=0; i<rhs->event_cnt; ++i) { 148 pj_strdup(pool, &hdr->events[i], &rhs->events[i]); 149 } 150 return hdr; 151 } 152 153 static pjsip_allow_events_hdr* 154 pjsip_allow_events_hdr_shallow_clone(pj_pool_t *pool, 155 const pjsip_allow_events_hdr *rhs) 156 { 157 pjsip_allow_events_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); 158 pj_memcpy(hdr, rhs, sizeof(*hdr)); 159 return hdr; 160 } 161 162 108 const pj_str_t STR_ALLOW_EVENTS = { "Allow-Events", 12}; 109 pjsip_allow_events_hdr *hdr; 110 111 hdr = pjsip_generic_array_hdr_create(pool, &STR_ALLOW_EVENTS); 112 113 if (hdr) { 114 hdr->sname.ptr = "u"; 115 hdr->sname.slen = 1; 116 } 117 118 return hdr; 119 } 120 121 122 /* 123 * Subscription-State header. 124 */ 163 125 static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr, 164 126 char *buf, pj_size_t size); … … 181 143 { 182 144 pj_str_t sub_state = { "Subscription-State", 18 }; 183 pjsip_sub_state_hdr *hdr = pj_pool_ calloc(pool, 1, sizeof(*hdr));145 pjsip_sub_state_hdr *hdr = pj_pool_zalloc(pool, sizeof(*hdr)); 184 146 hdr->type = PJSIP_H_OTHER; 185 147 hdr->name = hdr->sname = sub_state; … … 188 150 hdr->retry_after = -1; 189 151 pj_list_init(hdr); 152 pj_list_init(&hdr->other_param); 190 153 return hdr; 191 154 } … … 216 179 p += printed; 217 180 } 218 if (hdr->other_param.slen) 219 copy_advance(p, hdr->other_param); 181 182 printed = pjsip_param_print_on( &hdr->other_param, p, endbuf-p, 183 &pjsip_PARAM_CHAR_SPEC, 184 &pjsip_PARAM_CHAR_SPEC, 185 ';'); 186 if (printed < 0) 187 return printed; 188 189 p += printed; 220 190 221 191 return p - buf; … … 231 201 hdr->retry_after = rhs->retry_after; 232 202 hdr->expires_param = rhs->expires_param; 233 pj _strdup(pool, &hdr->other_param, &rhs->other_param);203 pjsip_param_clone(pool, &hdr->other_param, &rhs->other_param); 234 204 return hdr; 235 205 } … … 241 211 pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); 242 212 pj_memcpy(hdr, rhs, sizeof(*hdr)); 243 return hdr; 244 } 245 246 static pjsip_event_hdr *parse_hdr_event(pj_scanner *scanner, 247 pj_pool_t *pool) 248 { 249 pjsip_event_hdr *hdr = pjsip_event_hdr_create(pool); 213 pjsip_param_shallow_clone(pool, &hdr->other_param, &rhs->other_param); 214 return hdr; 215 } 216 217 218 /* 219 * Parse Event header. 220 */ 221 static pjsip_hdr *parse_hdr_event(pjsip_parse_ctx *ctx) 222 { 223 pjsip_event_hdr *hdr = pjsip_event_hdr_create(ctx->pool); 250 224 const pj_str_t id_param = { "id", 2 }; 251 225 252 pj_scan_get( scanner,pjsip_TOKEN_SPEC, &hdr->event_type);253 254 while (* scanner->current== ';') {226 pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->event_type); 227 228 while (*ctx->scanner->curptr == ';') { 255 229 pj_str_t pname, pvalue; 256 pj_scan_get_char(scanner); 257 pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); 230 231 pj_scan_get_char(ctx->scanner); 232 pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); 233 258 234 if (pj_stricmp(&pname, &id_param)==0) { 259 235 hdr->id_param = pvalue; 260 236 } else { 261 pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); 237 pjsip_param *param = pj_pool_alloc(ctx->pool, sizeof(pjsip_param)); 238 param->name = pname; 239 param->value = pvalue; 240 pj_list_push_back(&hdr->other_param, param); 262 241 } 263 242 } 264 pjsip_parse_end_hdr_imp( scanner ); 265 return hdr; 266 } 267 268 static pjsip_allow_events_hdr *parse_hdr_allow_events(pj_scanner *scanner, 269 pj_pool_t *pool) 270 { 271 pjsip_allow_events_hdr *hdr = pjsip_allow_events_hdr_create(pool); 272 273 pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[0]); 274 hdr->event_cnt = 1; 275 276 while (*scanner->current == ',') { 277 pj_scan_get_char(scanner); 278 pj_scan_get(scanner, pjsip_TOKEN_SPEC, &hdr->events[hdr->event_cnt++]); 279 if (hdr->event_cnt == PJSIP_MAX_ALLOW_EVENTS) { 280 PJ_THROW(PJSIP_SYN_ERR_EXCEPTION); 281 } 282 } 283 284 pjsip_parse_end_hdr_imp( scanner ); 285 return hdr; 286 } 287 288 static pjsip_sub_state_hdr *parse_hdr_sub_state(pj_scanner *scanner, 289 pj_pool_t *pool) 290 { 291 pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(pool); 243 pjsip_parse_end_hdr_imp( ctx->scanner ); 244 return (pjsip_hdr*)hdr; 245 } 246 247 /* 248 * Parse Subscription-State header. 249 */ 250 static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) 251 { 252 pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(ctx->pool); 292 253 const pj_str_t reason = { "reason", 6 }, 293 254 expires = { "expires", 7 }, 294 255 retry_after = { "retry-after", 11 }; 295 pj_scan_get( scanner,pjsip_TOKEN_SPEC, &hdr->sub_state);296 297 while (* scanner->current== ';') {256 pj_scan_get(ctx->scanner, &pjsip_TOKEN_SPEC, &hdr->sub_state); 257 258 while (*ctx->scanner->curptr == ';') { 298 259 pj_str_t pname, pvalue; 299 260 300 pj_scan_get_char(scanner); 301 pjsip_parse_param_imp(scanner, &pname, &pvalue, 0); 261 pj_scan_get_char(ctx->scanner); 262 pjsip_parse_param_imp(ctx->scanner, ctx->pool, &pname, &pvalue, 0); 263 302 264 if (pj_stricmp(&pname, &reason) == 0) { 303 265 hdr->reason_param = pvalue; 266 304 267 } else if (pj_stricmp(&pname, &expires) == 0) { 305 268 hdr->expires_param = pj_strtoul(&pvalue); 269 306 270 } else if (pj_stricmp(&pname, &retry_after) == 0) { 307 271 hdr->retry_after = pj_strtoul(&pvalue); 272 308 273 } else { 309 pjsip_concat_param_imp(&hdr->other_param, pool, &pname, &pvalue, ';'); 274 pjsip_param *param = pj_pool_alloc(ctx->pool, sizeof(pjsip_param)); 275 param->name = pname; 276 param->value = pvalue; 277 pj_list_push_back(&hdr->other_param, param); 310 278 } 311 279 } 312 280 313 pjsip_parse_end_hdr_imp( scanner ); 314 return hdr; 315 } 316 317 PJ_DEF(void) pjsip_event_notify_init_parser(void) 318 { 319 pjsip_register_hdr_parser( "Event", NULL, (pjsip_parse_hdr_func*) &parse_hdr_event); 320 pjsip_register_hdr_parser( "Allow-Events", NULL, (pjsip_parse_hdr_func*) &parse_hdr_allow_events); 321 pjsip_register_hdr_parser( "Subscription-State", NULL, (pjsip_parse_hdr_func*) &parse_hdr_sub_state); 322 } 281 pjsip_parse_end_hdr_imp( ctx->scanner ); 282 return (pjsip_hdr*)hdr; 283 } 284 285 /* 286 * Register header parsers. 287 */ 288 PJ_DEF(void) pjsip_evsub_init_parser(void) 289 { 290 pjsip_register_hdr_parser( "Event", NULL, 291 &parse_hdr_event); 292 293 pjsip_register_hdr_parser( "Subscription-State", NULL, 294 &parse_hdr_sub_state); 295 } 296 -
pjproject/trunk/pjsip/src/pjsip-simple/pidf.c
- Property svn:keywords set to id
r65 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip _simple/pidf.h>19 #include <pjsip-simple/pidf.h> 20 20 #include <pj/string.h> 21 21 #include <pj/pool.h> 22 #include <pj/assert.h> 23 22 24 23 25 struct pjpidf_op_desc pjpidf_op = … … 133 135 PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) 134 136 { 135 PJ_UNUSED_ARG(pres) 137 PJ_UNUSED_ARG(pres); 136 138 pj_list_erase(t); 137 139 } -
pjproject/trunk/pjsip/src/pjsip-simple/presence.c
- Property svn:keywords set to id
r65 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip_simple/presence.h> 20 #include <pjsip/sip_transport.h> 19 #include <pjsip-simple/presence.h> 20 #include <pjsip-simple/errno.h> 21 #include <pjsip-simple/evsub_msg.h> 22 #include <pjsip/sip_module.h> 23 #include <pjsip/sip_endpoint.h> 24 #include <pjsip/sip_dialog.h> 25 #include <pj/assert.h> 26 #include <pj/guid.h> 27 #include <pj/log.h> 28 #include <pj/os.h> 21 29 #include <pj/pool.h> 22 30 #include <pj/string.h> 23 #include <pj/guid.h> 24 #include <pj/os.h> 25 #include <stdio.h> 26 27 /* Forward declarations. */ 28 static void on_query_subscribe(pjsip_rx_data *rdata, int *status); 29 static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, 30 pjsip_event_sub_cb **cb, int *expires); 31 static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason); 32 static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata); 33 static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata); 34 35 /* Some string constants. */ 36 static pj_str_t PRESENCE_EVENT = { "presence", 8 }; 37 38 /* Accept types. */ 39 static pj_str_t accept_names[] = { 40 { "application/pidf+xml", 20 }, 41 { "application/xpidf+xml", 21 } 31 32 33 #define THIS_FILE "presence.c" 34 #define PRES_DEFAULT_EXPIRES 600 35 36 /* 37 * Presence module (mod-presence) 38 */ 39 static struct pjsip_module mod_presence = 40 { 41 NULL, NULL, /* prev, next. */ 42 { "mod-presence", 12 }, /* Name. */ 43 -1, /* Id */ 44 PJSIP_MOD_PRIORITY_APPLICATION-1, /* Priority */ 45 NULL, /* User data. */ 46 NULL, /* load() */ 47 NULL, /* start() */ 48 NULL, /* stop() */ 49 NULL, /* unload() */ 50 NULL, /* on_rx_request() */ 51 NULL, /* on_rx_response() */ 52 NULL, /* on_tx_request. */ 53 NULL, /* on_tx_response() */ 54 NULL, /* on_tsx_state() */ 42 55 }; 43 static pjsip_media_type accept_types[] = { 44 { 45 { "application", 11 }, 46 { "pidf+xml", 8 } 47 }, 48 { 49 { "application", 11 }, 50 { "xpidf+xml", 9 } 51 } 56 57 58 /* 59 * Presence message body type. 60 */ 61 typedef enum content_type 62 { 63 CONTENT_TYPE_NONE, 64 CONTENT_TYPE_PIDF, 65 CONTENT_TYPE_XPIDF, 66 } content_type; 67 68 /* 69 * This structure describe a presentity, for both subscriber and notifier. 70 */ 71 struct pjsip_pres 72 { 73 pjsip_evsub *sub; /**< Event subscribtion record. */ 74 pjsip_dialog *dlg; /**< The dialog. */ 75 content_type content_type; /**< Content-Type. */ 76 pjsip_pres_status status; /**< Presence status. */ 77 pjsip_pres_status tmp_status; /**< Temp, before NOTIFY is answred.*/ 78 pjsip_evsub_user user_cb; /**< The user callback. */ 52 79 }; 53 80 54 /* Callback that is registered by application. */ 55 static pjsip_presence_cb cb; 56 57 /* Package callback to be register to event_notify */ 58 static pjsip_event_sub_pkg_cb pkg_cb = { &on_query_subscribe, 59 &on_subscribe }; 60 61 /* Global/static callback to be registered to event_notify */ 62 static pjsip_event_sub_cb sub_cb = { &on_sub_terminated, 63 &on_sub_received_refresh, 64 NULL, 65 &on_received_notify, 66 NULL }; 67 68 /* 69 * Initialize presence module. 70 * This will register event package "presence" to event framework. 71 */ 72 PJ_DEF(void) pjsip_presence_init(const pjsip_presence_cb *pcb) 73 { 74 pj_memcpy(&cb, pcb, sizeof(*pcb)); 75 pjsip_event_sub_register_pkg( &PRESENCE_EVENT, 76 sizeof(accept_names)/sizeof(accept_names[0]), 77 accept_names, 78 &pkg_cb); 79 } 80 81 /* 82 * Create presence subscription. 83 */ 84 PJ_DEF(pjsip_presentity*) pjsip_presence_create( pjsip_endpoint *endpt, 85 const pj_str_t *local_url, 86 const pj_str_t *remote_url, 87 int expires, 88 void *user_data ) 89 { 90 pjsip_event_sub *sub; 91 pjsip_presentity *pres; 92 93 if (expires < 0) 94 expires = 300; 81 82 typedef struct pjsip_pres pjsip_pres; 83 84 85 /* 86 * Forward decl for evsub callback. 87 */ 88 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); 89 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, 90 pjsip_event *event); 91 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, 92 pjsip_rx_data *rdata, 93 int *p_st_code, 94 pj_str_t **p_st_text, 95 pjsip_hdr *res_hdr, 96 pjsip_msg_body **p_body); 97 static void pres_on_evsub_rx_notify( pjsip_evsub *sub, 98 pjsip_rx_data *rdata, 99 int *p_st_code, 100 pj_str_t **p_st_text, 101 pjsip_hdr *res_hdr, 102 pjsip_msg_body **p_body); 103 static void pres_on_evsub_client_refresh(pjsip_evsub *sub); 104 static void pres_on_evsub_server_timeout(pjsip_evsub *sub); 105 106 107 /* 108 * Event subscription callback for presence. 109 */ 110 static pjsip_evsub_user pres_user = 111 { 112 &pres_on_evsub_state, 113 &pres_on_evsub_tsx_state, 114 &pres_on_evsub_rx_refresh, 115 &pres_on_evsub_rx_notify, 116 &pres_on_evsub_client_refresh, 117 &pres_on_evsub_server_timeout, 118 }; 119 120 121 /* 122 * Some static constants. 123 */ 124 const pj_str_t STR_EVENT = { "Event", 5 }; 125 const pj_str_t STR_PRESENCE = { "presence", 8 }; 126 const pj_str_t STR_APPLICATION = { "application", 11 }; 127 const pj_str_t STR_PIDF_XML = { "pidf+xml", 8}; 128 const pj_str_t STR_XPIDF_XML = { "xpidf+xml", 9}; 129 const pj_str_t STR_APP_PIDF_XML = { "application/pidf+xml", 20 }; 130 const pj_str_t STR_APP_XPIDF_XML = { "application/xpidf+xml", 21 }; 131 132 133 /* 134 * Init presence module. 135 */ 136 PJ_DEF(pj_status_t) pjsip_pres_init_module( pjsip_endpoint *endpt, 137 pjsip_module *mod_evsub) 138 { 139 pj_status_t status; 140 pj_str_t accept[2]; 141 142 /* Check arguments. */ 143 PJ_ASSERT_RETURN(endpt && mod_evsub, PJ_EINVAL); 144 145 /* Must have not been registered */ 146 PJ_ASSERT_RETURN(mod_presence.id == -1, PJ_EINVALIDOP); 147 148 /* Register to endpoint */ 149 status = pjsip_endpt_register_module(endpt, &mod_presence); 150 if (status != PJ_SUCCESS) 151 return status; 152 153 accept[0] = STR_APP_PIDF_XML; 154 accept[1] = STR_APP_XPIDF_XML; 155 156 /* Register event package to event module. */ 157 status = pjsip_evsub_register_pkg( &mod_presence, &STR_PRESENCE, 158 PRES_DEFAULT_EXPIRES, 159 PJ_ARRAY_SIZE(accept), accept); 160 if (status != PJ_SUCCESS) { 161 pjsip_endpt_unregister_module(endpt, &mod_presence); 162 return status; 163 } 164 165 return PJ_SUCCESS; 166 } 167 168 169 /* 170 * Get presence module instance. 171 */ 172 PJ_DEF(pjsip_module*) pjsip_pres_instance(void) 173 { 174 return &mod_presence; 175 } 176 177 178 /* 179 * Create client subscription. 180 */ 181 PJ_DEF(pj_status_t) pjsip_pres_create_uac( pjsip_dialog *dlg, 182 const pjsip_evsub_user *user_cb, 183 pjsip_evsub **p_evsub ) 184 { 185 pj_status_t status; 186 pjsip_pres *pres; 187 pjsip_evsub *sub; 188 189 PJ_ASSERT_RETURN(dlg && p_evsub, PJ_EINVAL); 190 191 pjsip_dlg_inc_lock(dlg); 95 192 96 193 /* Create event subscription */ 97 sub = pjsip_event_sub_create(endpt, local_url, remote_url, &PRESENCE_EVENT, 98 expires, 99 sizeof(accept_names)/sizeof(accept_names[0]), 100 accept_names, 101 NULL, &sub_cb); 102 if (!sub) 103 return NULL; 104 105 /* Allocate presence descriptor. */ 106 pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); 107 pres->sub = sub; 108 pres->user_data = user_data; 109 sub->user_data = pres; 110 111 return pres; 112 } 113 114 /* 115 * Send SUBSCRIBE. 116 */ 117 PJ_DEF(pj_status_t) pjsip_presence_subscribe( pjsip_presentity *pres ) 118 { 119 return pjsip_event_sub_subscribe( pres->sub ); 120 } 121 122 /* 123 * Set credentials to be used for outgoing requests. 124 */ 125 PJ_DEF(pj_status_t) pjsip_presence_set_credentials( pjsip_presentity *pres, 126 int count, 127 const pjsip_cred_info cred[]) 128 { 129 return pjsip_event_sub_set_credentials(pres->sub, count, cred); 130 } 131 132 /* 133 * Set route-set. 134 */ 135 PJ_DEF(pj_status_t) pjsip_presence_set_route_set( pjsip_presentity *pres, 136 const pjsip_route_hdr *hdr ) 137 { 138 return pjsip_event_sub_set_route_set( pres->sub, hdr ); 139 } 140 141 /* 142 * Unsubscribe. 143 */ 144 PJ_DEF(pj_status_t) pjsip_presence_unsubscribe( pjsip_presentity *pres ) 145 { 146 return pjsip_event_sub_unsubscribe(pres->sub); 147 } 148 149 /* 150 * This is the pjsip_msg_body callback to print XML body. 151 */ 152 static int print_xml(pjsip_msg_body *body, char *buf, pj_size_t size) 153 { 154 return pj_xml_print( body->data, buf, size, PJ_TRUE ); 155 } 156 157 /* 158 * Create and initialize PIDF document and msg body (notifier only). 159 */ 160 static pj_status_t init_presence_info( pjsip_presentity *pres ) 161 { 162 pj_str_t uri; 163 pj_pool_t *pool = pres->sub->pool; 164 char tmp[PJSIP_MAX_URL_SIZE]; 165 pjpidf_tuple *tuple; 166 const pjsip_media_type *content_type = NULL; 167 168 pj_assert(pres->uas_body == NULL); 169 170 /* Make entity_id */ 171 uri.ptr = tmp; 172 uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->from->uri, 173 tmp, sizeof(tmp)); 174 if (uri.slen < 0) 175 return -1; 176 177 if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { 178 pj_str_t s; 179 180 /* Create <presence>. */ 181 pres->uas_data.pidf = pjpidf_create(pool, &s); 182 183 /* Create <tuple> */ 184 pj_create_unique_string(pool, &s); 185 tuple = pjpidf_pres_add_tuple(pool, pres->uas_data.pidf, &s); 186 187 /* Set <contact> */ 188 s.ptr = tmp; 189 s.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, pres->sub->contact->uri, tmp, sizeof(tmp)); 190 if (s.slen < 0) 191 return -1; 192 pjpidf_tuple_set_contact(pool, tuple, &s); 193 194 /* Content-Type */ 195 content_type = &accept_types[PJSIP_PRES_TYPE_PIDF]; 196 197 } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { 198 199 /* Create XPIDF */ 200 pres->uas_data.xpidf = pjxpidf_create(pool, &uri); 201 202 /* Content-Type. */ 203 content_type = &accept_types[PJSIP_PRES_TYPE_XPIDF]; 204 } 205 206 /* Create message body */ 207 pres->uas_body = pj_pool_alloc(pool, sizeof(pjsip_msg_body)); 208 pres->uas_body->content_type = *content_type; 209 pres->uas_body->data = pres->uas_data.pidf; 210 pres->uas_body->len = 0; 211 pres->uas_body->print_body = &print_xml; 212 213 return 0; 214 } 215 216 /* 217 * Send NOTIFY and set subscription state. 218 */ 219 PJ_DEF(pj_status_t) pjsip_presence_notify( pjsip_presentity *pres, 220 pjsip_event_sub_state state, 221 pj_bool_t is_online ) 222 { 223 pj_str_t reason = { "", 0 }; 224 225 if (pres->uas_data.pidf == NULL) { 226 if (init_presence_info(pres) != 0) 227 return -1; 228 } 229 230 /* Update basic status in PIDF/XPIDF document. */ 231 if (pres->pres_type == PJSIP_PRES_TYPE_PIDF) { 232 pjpidf_tuple *first; 233 pjpidf_status *status; 234 pj_time_val now; 235 pj_parsed_time pnow; 236 237 first = pjpidf_op.pres.get_first_tuple(pres->uas_data.pidf); 238 pj_assert(first); 239 status = pjpidf_op.tuple.get_status(first); 240 pj_assert(status); 241 pjpidf_op.status.set_basic_open(status, is_online); 242 243 /* Update timestamp. */ 244 if (pres->timestamp.ptr == 0) { 245 pres->timestamp.ptr = pj_pool_alloc(pres->sub->pool, 24); 246 } 247 pj_gettimeofday(&now); 248 pj_time_decode(&now, &pnow); 249 pres->timestamp.slen = sprintf(pres->timestamp.ptr, 250 "%04d-%02d-%02dT%02d:%02d:%02dZ", 251 pnow.year, pnow.mon, pnow.day, 252 pnow.hour, pnow.min, pnow.sec); 253 pjpidf_op.tuple.set_timestamp_np(pres->sub->pool, first, &pres->timestamp); 254 255 } else if (pres->pres_type == PJSIP_PRES_TYPE_XPIDF) { 256 pjxpidf_set_status( pres->uas_data.xpidf, is_online ); 257 258 } else { 259 pj_assert(0); 260 } 261 262 /* Send notify. */ 263 return pjsip_event_sub_notify( pres->sub, state, &reason, pres->uas_body); 264 } 265 266 /* 267 * Destroy subscription (can be called for both subscriber and notifier). 268 */ 269 PJ_DEF(pj_status_t) pjsip_presence_destroy( pjsip_presentity *pres ) 270 { 271 return pjsip_event_sub_destroy(pres->sub); 272 } 273 274 /* 275 * This callback is called by event framework to query whether we want to 276 * accept an incoming subscription. 277 */ 278 static void on_query_subscribe(pjsip_rx_data *rdata, int *status) 279 { 280 if (cb.accept_presence) { 281 (*cb.accept_presence)(rdata, status); 282 } 283 } 284 285 /* 286 * This callback is called by event framework after we accept the incoming 287 * subscription, to notify about the new subscription instance. 288 */ 289 static void on_subscribe(pjsip_event_sub *sub, pjsip_rx_data *rdata, 290 pjsip_event_sub_cb **set_sub_cb, int *expires) 291 { 292 pjsip_presentity *pres; 194 status = pjsip_evsub_create_uac( dlg, &pres_user, &STR_PRESENCE, &sub); 195 if (status != PJ_SUCCESS) 196 goto on_return; 197 198 /* Create presence */ 199 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres)); 200 pres->dlg = dlg; 201 if (user_cb) 202 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); 203 204 /* Attach to evsub */ 205 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); 206 207 *p_evsub = sub; 208 209 on_return: 210 pjsip_dlg_dec_lock(dlg); 211 return status; 212 } 213 214 215 /* 216 * Create server subscription. 217 */ 218 PJ_DEF(pj_status_t) pjsip_pres_create_uas( pjsip_dialog *dlg, 219 const pjsip_evsub_user *user_cb, 220 pjsip_rx_data *rdata, 221 pjsip_evsub **p_evsub ) 222 { 293 223 pjsip_accept_hdr *accept; 294 295 pres = pj_pool_calloc(sub->pool, 1, sizeof(*pres)); 296 pres->sub = sub; 297 pres->pres_type = PJSIP_PRES_TYPE_PIDF; 298 sub->user_data = pres; 299 *set_sub_cb = &sub_cb; 300 301 accept = pjsip_msg_find_hdr(rdata->msg, PJSIP_H_ACCEPT, NULL); 224 pjsip_event_hdr *event; 225 pjsip_expires_hdr *expires_hdr; 226 unsigned expires; 227 content_type content_type = CONTENT_TYPE_NONE; 228 pjsip_evsub *sub; 229 pjsip_pres *pres; 230 pj_status_t status; 231 232 /* Check arguments */ 233 PJ_ASSERT_RETURN(dlg && rdata && p_evsub, PJ_EINVAL); 234 235 /* Must be request message */ 236 PJ_ASSERT_RETURN(rdata->msg_info.msg->type == PJSIP_REQUEST_MSG, 237 PJSIP_ENOTREQUESTMSG); 238 239 /* Check that request is SUBSCRIBE */ 240 PJ_ASSERT_RETURN(pjsip_method_cmp(&rdata->msg_info.msg->line.req.method, 241 &pjsip_subscribe_method)==0, 242 PJSIP_SIMPLE_ENOTSUBSCRIBE); 243 244 /* Check that Event header contains "presence" */ 245 event = pjsip_msg_find_hdr_by_name(rdata->msg_info.msg, &STR_EVENT, NULL); 246 if (!event) { 247 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_REQUEST); 248 } 249 if (pj_stricmp(&event->event_type, &STR_PRESENCE) != 0) { 250 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EVENT); 251 } 252 253 /* Check that request contains compatible Accept header. */ 254 accept = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_ACCEPT, NULL); 302 255 if (accept) { 303 256 unsigned i; 304 int found = 0; 305 for (i=0; i<accept->count && !found; ++i) { 306 int j; 307 for (j=0; j<sizeof(accept_names)/sizeof(accept_names[0]); ++j) { 308 if (!pj_stricmp(&accept->values[i], &accept_names[j])) { 309 pres->pres_type = j; 310 found = 1; 311 break; 312 } 313 } 314 } 315 pj_assert(found ); 316 } 317 318 (*cb.on_received_request)(pres, rdata, expires); 319 } 320 321 /* 322 * This callback is called by event framework when the subscription is 323 * terminated. 324 */ 325 static void on_sub_terminated(pjsip_event_sub *sub, const pj_str_t *reason) 326 { 327 pjsip_presentity *pres = sub->user_data; 328 if (cb.on_terminated) 329 (*cb.on_terminated)(pres, reason); 330 } 331 332 /* 333 * This callback is called by event framework when it receives incoming 334 * SUBSCRIBE request to refresh the subscription. 335 */ 336 static void on_sub_received_refresh(pjsip_event_sub *sub, pjsip_rx_data *rdata) 337 { 338 pjsip_presentity *pres = sub->user_data; 339 if (cb.on_received_refresh) 340 (*cb.on_received_refresh)(pres, rdata); 341 } 342 343 /* 344 * This callback is called by event framework when it receives incoming 345 * NOTIFY request. 346 */ 347 static void on_received_notify(pjsip_event_sub *sub, pjsip_rx_data *rdata) 348 { 349 pjsip_presentity *pres = sub->user_data; 350 351 if (cb.on_received_update) { 352 pj_status_t is_open; 353 pjsip_msg_body *body; 354 int i; 355 356 body = rdata->msg->body; 357 if (!body) 358 return; 359 360 for (i=0; i<sizeof(accept_types)/sizeof(accept_types[0]); ++i) { 361 if (!pj_stricmp(&body->content_type.type, &accept_types[i].type) && 362 !pj_stricmp(&body->content_type.subtype, &accept_types[i].subtype)) 363 { 257 for (i=0; i<accept->count; ++i) { 258 if (pj_stricmp(&accept->values[i], &STR_APP_PIDF_XML)==0) { 259 content_type = CONTENT_TYPE_PIDF; 260 break; 261 } else 262 if (pj_stricmp(&accept->values[i], &STR_APP_XPIDF_XML)==0) { 263 content_type = CONTENT_TYPE_XPIDF; 364 264 break; 365 265 } 366 266 } 367 267 368 if (i==PJSIP_PRES_TYPE_PIDF) { 369 pjpidf_pres *pres; 370 pjpidf_tuple *tuple; 371 pjpidf_status *status; 372 373 pres = pjpidf_parse(rdata->pool, body->data, body->len); 374 if (!pres) 375 return; 376 tuple = pjpidf_pres_get_first_tuple(pres); 377 if (!tuple) 378 return; 379 status = pjpidf_tuple_get_status(tuple); 380 if (!status) 381 return; 382 is_open = pjpidf_status_is_basic_open(status); 383 384 } else if (i==PJSIP_PRES_TYPE_XPIDF) { 385 pjxpidf_pres *pres; 386 387 pres = pjxpidf_parse(rdata->pool, body->data, body->len); 388 if (!pres) 389 return; 390 is_open = pjxpidf_get_status(pres); 391 268 if (i==accept->count) { 269 /* Nothing is acceptable */ 270 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_NOT_ACCEPTABLE); 271 } 272 273 } else { 274 /* No Accept header. 275 * Treat as "application/pidf+xml" 276 */ 277 content_type = CONTENT_TYPE_PIDF; 278 } 279 280 /* Check that expires is not too short. */ 281 expires_hdr=pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_EXPIRES, NULL); 282 if (expires_hdr) { 283 if (expires_hdr->ivalue < 5) { 284 return PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_INTERVAL_TOO_BRIEF); 285 } 286 287 expires = expires_hdr->ivalue; 288 if (expires > PRES_DEFAULT_EXPIRES) 289 expires = PRES_DEFAULT_EXPIRES; 290 291 } else { 292 expires = PRES_DEFAULT_EXPIRES; 293 } 294 295 /* Lock dialog */ 296 pjsip_dlg_inc_lock(dlg); 297 298 299 /* Create server subscription */ 300 status = pjsip_evsub_create_uas( dlg, &pres_user, rdata, &sub); 301 if (status != PJ_SUCCESS) 302 goto on_return; 303 304 /* Create server presence subscription */ 305 pres = pj_pool_zalloc(dlg->pool, sizeof(pjsip_pres)); 306 pres->dlg = dlg; 307 pres->sub = sub; 308 pres->content_type = content_type; 309 if (user_cb) 310 pj_memcpy(&pres->user_cb, user_cb, sizeof(pjsip_evsub_user)); 311 312 /* Attach to evsub */ 313 pjsip_evsub_set_mod_data(sub, mod_presence.id, pres); 314 315 /* Done: */ 316 *p_evsub = sub; 317 318 on_return: 319 pjsip_dlg_dec_lock(dlg); 320 return status; 321 } 322 323 324 /* 325 * Create SUBSCRIBE 326 */ 327 PJ_DEF(pj_status_t) pjsip_pres_initiate( pjsip_evsub *sub, 328 pj_int32_t expires, 329 pjsip_tx_data **p_tdata) 330 { 331 return pjsip_evsub_initiate(sub, &pjsip_subscribe_method, expires, 332 p_tdata); 333 } 334 335 336 /* 337 * Accept incoming subscription. 338 */ 339 PJ_DEF(pj_status_t) pjsip_pres_accept( pjsip_evsub *sub, 340 pjsip_rx_data *rdata, 341 int st_code, 342 const pjsip_hdr *hdr_list ) 343 { 344 return pjsip_evsub_accept( sub, rdata, st_code, hdr_list ); 345 } 346 347 348 /* 349 * Get presence status. 350 */ 351 PJ_DEF(pj_status_t) pjsip_pres_get_status( pjsip_evsub *sub, 352 pjsip_pres_status *status ) 353 { 354 pjsip_pres *pres; 355 356 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); 357 358 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 359 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); 360 361 if (pres->tmp_status._is_valid) 362 pj_memcpy(status, &pres->tmp_status, sizeof(pjsip_pres_status)); 363 else 364 pj_memcpy(status, &pres->status, sizeof(pjsip_pres_status)); 365 366 return PJ_SUCCESS; 367 } 368 369 370 /* 371 * Set presence status. 372 */ 373 PJ_DEF(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, 374 const pjsip_pres_status *status ) 375 { 376 unsigned i; 377 pjsip_pres *pres; 378 379 PJ_ASSERT_RETURN(sub && status, PJ_EINVAL); 380 381 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 382 PJ_ASSERT_RETURN(pres!=NULL, PJSIP_SIMPLE_ENOPRESENCE); 383 384 for (i=0; i<status->info_cnt; ++i) { 385 pres->status.info[i].basic_open = status->info[i].basic_open; 386 if (status->info[i].id.slen == 0) { 387 pj_create_unique_string(pres->dlg->pool, 388 &pres->status.info[i].id); 392 389 } else { 393 return; 390 pj_strdup(pres->dlg->pool, 391 &pres->status.info[i].id, 392 &status->info[i].id); 394 393 } 395 396 (*cb.on_received_update)(pres, is_open); 397 } 398 } 399 394 pj_strdup(pres->dlg->pool, 395 &pres->status.info[i].contact, 396 &status->info[i].contact); 397 } 398 399 pres->status.info_cnt = status->info_cnt; 400 401 return PJ_SUCCESS; 402 } 403 404 405 /* 406 * Create PIDF document based on the presence info. 407 */ 408 static pjpidf_pres* pres_create_pidf( pj_pool_t *pool, 409 pjsip_pres *pres ) 410 { 411 pjpidf_pres *pidf; 412 unsigned i; 413 pj_str_t entity; 414 415 /* Get publisher URI */ 416 entity.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); 417 entity.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 418 pres->dlg->local.info->uri, 419 entity.ptr, PJSIP_MAX_URL_SIZE); 420 if (entity.slen < 1) 421 return NULL; 422 423 /* Create <presence>. */ 424 pidf = pjpidf_create(pool, &entity); 425 426 /* Create <tuple> */ 427 for (i=0; i<pres->status.info_cnt; ++i) { 428 429 pjpidf_tuple *pidf_tuple; 430 pjpidf_status *pidf_status; 431 432 /* Add tuple id. */ 433 pidf_tuple = pjpidf_pres_add_tuple(pool, pidf, 434 &pres->status.info[i].id); 435 436 /* Set <contact> */ 437 if (pres->status.info[i].contact.slen) 438 pjpidf_tuple_set_contact(pool, pidf_tuple, 439 &pres->status.info[i].contact); 440 441 442 /* Set basic status */ 443 pidf_status = pjpidf_tuple_get_status(pidf_tuple); 444 pjpidf_status_set_basic_open(pidf_status, 445 pres->status.info[i].basic_open); 446 } 447 448 return pidf; 449 } 450 451 452 /* 453 * Create XPIDF document based on the presence info. 454 */ 455 static pjxpidf_pres* pres_create_xpidf( pj_pool_t *pool, 456 pjsip_pres *pres ) 457 { 458 /* Note: PJSIP implementation of XPIDF is not complete! 459 */ 460 pjxpidf_pres *xpidf; 461 pj_str_t publisher_uri; 462 463 PJ_LOG(4,(THIS_FILE, "Warning: XPIDF format is not fully supported " 464 "by PJSIP")); 465 466 publisher_uri.ptr = pj_pool_alloc(pool, PJSIP_MAX_URL_SIZE); 467 publisher_uri.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI, 468 pres->dlg->local.info->uri, 469 publisher_uri.ptr, 470 PJSIP_MAX_URL_SIZE); 471 if (publisher_uri.slen < 1) 472 return NULL; 473 474 /* Create XPIDF document. */ 475 xpidf = pjxpidf_create(pool, &publisher_uri); 476 477 /* Set basic status. */ 478 if (pres->status.info_cnt > 0) 479 pjxpidf_set_status( xpidf, pres->status.info[0].basic_open); 480 else 481 pjxpidf_set_status( xpidf, PJ_FALSE); 482 483 return xpidf; 484 } 485 486 487 /* 488 * Function to print XML message body. 489 */ 490 static int pres_print_body(struct pjsip_msg_body *msg_body, 491 char *buf, pj_size_t size) 492 { 493 return pj_xml_print(msg_body->data, buf, size, PJ_TRUE); 494 } 495 496 497 /* 498 * Function to clone XML document. 499 */ 500 static void* xml_clone_data(pj_pool_t *pool, const void *data, unsigned len) 501 { 502 PJ_UNUSED_ARG(len); 503 return pj_xml_clone( pool, data); 504 } 505 506 507 /* 508 * Create message body. 509 */ 510 static pj_status_t pres_create_msg_body( pjsip_pres *pres, 511 pjsip_tx_data *tdata) 512 { 513 pjsip_msg_body *body; 514 515 body = pj_pool_zalloc(tdata->pool, sizeof(pjsip_msg_body)); 516 517 if (pres->content_type == CONTENT_TYPE_PIDF) { 518 519 body->data = pres_create_pidf(tdata->pool, pres); 520 body->content_type.type = pj_str("application"); 521 body->content_type.subtype = pj_str("pidf+xml"); 522 523 } else if (pres->content_type == CONTENT_TYPE_XPIDF) { 524 525 body->data = pres_create_xpidf(tdata->pool, pres); 526 body->content_type.type = pj_str("application"); 527 body->content_type.subtype = pj_str("xpidf+xml"); 528 529 } else { 530 return PJSIP_SIMPLE_EBADCONTENT; 531 } 532 533 534 body->print_body = &pres_print_body; 535 body->clone_data = &xml_clone_data; 536 537 tdata->msg->body = body; 538 539 return PJ_SUCCESS; 540 } 541 542 543 /* 544 * Create NOTIFY 545 */ 546 PJ_DEF(pj_status_t) pjsip_pres_notify( pjsip_evsub *sub, 547 pjsip_evsub_state state, 548 const pj_str_t *state_str, 549 const pj_str_t *reason, 550 pjsip_tx_data **p_tdata) 551 { 552 pjsip_pres *pres; 553 pjsip_tx_data *tdata; 554 pj_status_t status; 555 556 /* Check arguments. */ 557 PJ_ASSERT_RETURN(sub, PJ_EINVAL); 558 559 /* Get the presence object. */ 560 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 561 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); 562 563 /* Must have at least one presence info. */ 564 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); 565 566 567 /* Lock object. */ 568 pjsip_dlg_inc_lock(pres->dlg); 569 570 /* Create the NOTIFY request. */ 571 status = pjsip_evsub_notify( sub, state, state_str, reason, &tdata); 572 if (status != PJ_SUCCESS) 573 goto on_return; 574 575 576 /* Create message body to reflect the presence status. */ 577 status = pres_create_msg_body( pres, tdata ); 578 if (status != PJ_SUCCESS) 579 goto on_return; 580 581 582 /* Done. */ 583 *p_tdata = tdata; 584 585 586 on_return: 587 pjsip_dlg_dec_lock(pres->dlg); 588 return status; 589 } 590 591 592 /* 593 * Create NOTIFY that reflect current state. 594 */ 595 PJ_DEF(pj_status_t) pjsip_pres_current_notify( pjsip_evsub *sub, 596 pjsip_tx_data **p_tdata ) 597 { 598 pjsip_pres *pres; 599 pjsip_tx_data *tdata; 600 pj_status_t status; 601 602 /* Check arguments. */ 603 PJ_ASSERT_RETURN(sub, PJ_EINVAL); 604 605 /* Get the presence object. */ 606 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 607 PJ_ASSERT_RETURN(pres != NULL, PJSIP_SIMPLE_ENOPRESENCE); 608 609 /* Must have at least one presence info. */ 610 PJ_ASSERT_RETURN(pres->status.info_cnt > 0, PJSIP_SIMPLE_ENOPRESENCEINFO); 611 612 613 /* Lock object. */ 614 pjsip_dlg_inc_lock(pres->dlg); 615 616 /* Create the NOTIFY request. */ 617 status = pjsip_evsub_current_notify( sub, &tdata); 618 if (status != PJ_SUCCESS) 619 goto on_return; 620 621 622 /* Create message body to reflect the presence status. */ 623 status = pres_create_msg_body( pres, tdata ); 624 if (status != PJ_SUCCESS) 625 goto on_return; 626 627 628 /* Done. */ 629 *p_tdata = tdata; 630 631 632 on_return: 633 pjsip_dlg_dec_lock(pres->dlg); 634 return status; 635 } 636 637 638 /* 639 * Send request. 640 */ 641 PJ_DEF(pj_status_t) pjsip_pres_send_request( pjsip_evsub *sub, 642 pjsip_tx_data *tdata ) 643 { 644 return pjsip_evsub_send_request(sub, tdata); 645 } 646 647 648 /* 649 * This callback is called by event subscription when subscription 650 * state has changed. 651 */ 652 static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event) 653 { 654 pjsip_pres *pres; 655 656 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 657 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 658 659 if (pres->user_cb.on_evsub_state) 660 (*pres->user_cb.on_evsub_state)(sub, event); 661 } 662 663 /* 664 * Called when transaction state has changed. 665 */ 666 static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, 667 pjsip_event *event) 668 { 669 pjsip_pres *pres; 670 671 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 672 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 673 674 if (pres->user_cb.on_tsx_state) 675 (*pres->user_cb.on_tsx_state)(sub, tsx, event); 676 } 677 678 679 /* 680 * Called when SUBSCRIBE is received. 681 */ 682 static void pres_on_evsub_rx_refresh( pjsip_evsub *sub, 683 pjsip_rx_data *rdata, 684 int *p_st_code, 685 pj_str_t **p_st_text, 686 pjsip_hdr *res_hdr, 687 pjsip_msg_body **p_body) 688 { 689 pjsip_pres *pres; 690 691 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 692 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 693 694 if (pres->user_cb.on_rx_refresh) { 695 (*pres->user_cb.on_rx_refresh)(sub, rdata, p_st_code, p_st_text, 696 res_hdr, p_body); 697 698 } else { 699 /* Implementors MUST send NOTIFY if it implements on_rx_refresh */ 700 pjsip_tx_data *tdata; 701 pj_str_t timeout = { "timeout", 7}; 702 pj_status_t status; 703 704 if (pjsip_evsub_get_state(sub)==PJSIP_EVSUB_STATE_TERMINATED) { 705 status = pjsip_pres_notify( sub, PJSIP_EVSUB_STATE_TERMINATED, 706 NULL, &timeout, &tdata); 707 } else { 708 status = pjsip_pres_current_notify(sub, &tdata); 709 } 710 711 if (status == PJ_SUCCESS) 712 pjsip_pres_send_request(sub, tdata); 713 } 714 } 715 716 /* 717 * Parse PIDF to info. 718 */ 719 static pj_status_t pres_parse_pidf( pjsip_pres *pres, 720 pjsip_rx_data *rdata, 721 pjsip_pres_status *pres_status) 722 { 723 pjpidf_pres *pidf; 724 pjpidf_tuple *pidf_tuple; 725 726 pidf = pjpidf_parse(rdata->tp_info.pool, 727 rdata->msg_info.msg->body->data, 728 rdata->msg_info.msg->body->len); 729 if (pidf == NULL) 730 return PJSIP_SIMPLE_EBADPIDF; 731 732 pres_status->info_cnt = 0; 733 734 pidf_tuple = pjpidf_pres_get_first_tuple(pidf); 735 while (pidf_tuple) { 736 pjpidf_status *pidf_status; 737 738 pj_strdup(pres->dlg->pool, 739 &pres_status->info[pres_status->info_cnt].id, 740 pjpidf_tuple_get_id(pidf_tuple)); 741 742 pj_strdup(pres->dlg->pool, 743 &pres_status->info[pres_status->info_cnt].contact, 744 pjpidf_tuple_get_contact(pidf_tuple)); 745 746 pidf_status = pjpidf_tuple_get_status(pidf_tuple); 747 if (pidf_status) { 748 pres_status->info[pres_status->info_cnt].basic_open = 749 pjpidf_status_is_basic_open(pidf_status); 750 } else { 751 pres_status->info[pres_status->info_cnt].basic_open = PJ_FALSE; 752 } 753 754 pidf_tuple = pjpidf_pres_get_next_tuple( pidf, pidf_tuple ); 755 pres_status->info_cnt++; 756 } 757 758 return PJ_SUCCESS; 759 } 760 761 /* 762 * Parse XPIDF info. 763 */ 764 static pj_status_t pres_parse_xpidf( pjsip_pres *pres, 765 pjsip_rx_data *rdata, 766 pjsip_pres_status *pres_status) 767 { 768 pjxpidf_pres *xpidf; 769 770 xpidf = pjxpidf_parse(rdata->tp_info.pool, 771 rdata->msg_info.msg->body->data, 772 rdata->msg_info.msg->body->len); 773 if (xpidf == NULL) 774 return PJSIP_SIMPLE_EBADXPIDF; 775 776 pres_status->info_cnt = 1; 777 778 pj_strdup(pres->dlg->pool, 779 &pres_status->info[0].contact, 780 pjxpidf_get_uri(xpidf)); 781 pres_status->info[0].basic_open = pjxpidf_get_status(xpidf); 782 pres_status->info[0].id.slen = 0; 783 784 return PJ_SUCCESS; 785 } 786 787 788 /* 789 * Called when NOTIFY is received. 790 */ 791 static void pres_on_evsub_rx_notify( pjsip_evsub *sub, 792 pjsip_rx_data *rdata, 793 int *p_st_code, 794 pj_str_t **p_st_text, 795 pjsip_hdr *res_hdr, 796 pjsip_msg_body **p_body) 797 { 798 pjsip_ctype_hdr *ctype_hdr; 799 pjsip_pres *pres; 800 pj_status_t status; 801 802 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 803 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 804 805 /* Check Content-Type and msg body are present. */ 806 ctype_hdr = rdata->msg_info.ctype; 807 808 if (ctype_hdr==NULL || rdata->msg_info.msg->body==NULL) { 809 810 pjsip_warning_hdr *warn_hdr; 811 pj_str_t warn_text; 812 813 *p_st_code = PJSIP_SC_BAD_REQUEST; 814 815 warn_text = pj_str("Message body is not present"); 816 warn_hdr = pjsip_warning_hdr_create(rdata->tp_info.pool, 399, 817 pjsip_endpt_name(pres->dlg->endpt), 818 &warn_text); 819 pj_list_push_back(res_hdr, warn_hdr); 820 821 return; 822 } 823 824 /* Parse content. */ 825 826 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && 827 pj_stricmp(&ctype_hdr->media.subtype, &STR_PIDF_XML)==0) 828 { 829 status = pres_parse_pidf( pres, rdata, &pres->tmp_status); 830 } 831 else 832 if (pj_stricmp(&ctype_hdr->media.type, &STR_APPLICATION)==0 && 833 pj_stricmp(&ctype_hdr->media.subtype, &STR_XPIDF_XML)==0) 834 { 835 status = pres_parse_pidf( pres, rdata, &pres->tmp_status); 836 } 837 else 838 { 839 status = PJSIP_SIMPLE_EBADCONTENT; 840 } 841 842 if (status != PJ_SUCCESS) { 843 /* Unsupported or bad Content-Type */ 844 pjsip_accept_hdr *accept_hdr; 845 pjsip_warning_hdr *warn_hdr; 846 847 *p_st_code = PJSIP_SC_NOT_ACCEPTABLE_HERE; 848 849 /* Add Accept header */ 850 accept_hdr = pjsip_accept_hdr_create(rdata->tp_info.pool); 851 accept_hdr->values[accept_hdr->count++] = STR_APP_PIDF_XML; 852 accept_hdr->values[accept_hdr->count++] = STR_APP_XPIDF_XML; 853 pj_list_push_back(res_hdr, accept_hdr); 854 855 /* Add Warning header */ 856 warn_hdr = pjsip_warning_hdr_create_from_status( 857 rdata->tp_info.pool, 858 pjsip_endpt_name(pres->dlg->endpt), 859 status); 860 pj_list_push_back(res_hdr, warn_hdr); 861 862 return; 863 } 864 865 /* If application calls pres_get_status(), redirect the call to 866 * retrieve the temporary status. 867 */ 868 pres->tmp_status._is_valid = PJ_TRUE; 869 870 /* Notify application. */ 871 if (pres->user_cb.on_rx_notify) { 872 (*pres->user_cb.on_rx_notify)(sub, rdata, p_st_code, p_st_text, 873 res_hdr, p_body); 874 } 875 876 877 /* If application responded NOTIFY with 2xx, copy temporary status 878 * to main status, and mark the temporary status as invalid. 879 */ 880 if ((*p_st_code)/100 == 2) { 881 pj_memcpy(&pres->status, &pres->tmp_status, sizeof(pjsip_pres_status)); 882 } 883 884 pres->tmp_status._is_valid = PJ_FALSE; 885 886 /* Done */ 887 } 888 889 /* 890 * Called when it's time to send SUBSCRIBE. 891 */ 892 static void pres_on_evsub_client_refresh(pjsip_evsub *sub) 893 { 894 pjsip_pres *pres; 895 896 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 897 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 898 899 if (pres->user_cb.on_client_refresh) { 900 (*pres->user_cb.on_client_refresh)(sub); 901 } else { 902 pj_status_t status; 903 pjsip_tx_data *tdata; 904 905 status = pjsip_pres_initiate(sub, -1, &tdata); 906 if (status == PJ_SUCCESS) 907 pjsip_pres_send_request(sub, tdata); 908 } 909 } 910 911 /* 912 * Called when no refresh is received after the interval. 913 */ 914 static void pres_on_evsub_server_timeout(pjsip_evsub *sub) 915 { 916 pjsip_pres *pres; 917 918 pres = pjsip_evsub_get_mod_data(sub, mod_presence.id); 919 PJ_ASSERT_ON_FAIL(pres!=NULL, {return;}); 920 921 if (pres->user_cb.on_server_timeout) { 922 (*pres->user_cb.on_server_timeout)(sub); 923 } else { 924 pj_status_t status; 925 pjsip_tx_data *tdata; 926 pj_str_t reason = { "timeout", 7 }; 927 928 status = pjsip_pres_notify(sub, PJSIP_EVSUB_STATE_TERMINATED, 929 NULL, &reason, &tdata); 930 if (status == PJ_SUCCESS) 931 pjsip_pres_send_request(sub, tdata); 932 } 933 } 934 -
pjproject/trunk/pjsip/src/pjsip-simple/xpidf.c
- Property svn:keywords set to id
r65 r197 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 #include <pjsip_simple/xpidf.h> 19 #include <pjsip-simple/xpidf.h> 20 #include <pj/assert.h> 21 #include <pj/guid.h> 20 22 #include <pj/pool.h> 21 23 #include <pj/string.h> 22 #include <pj/guid.h>23 24 24 25 static pj_str_t PRESENCE = { "presence", 8 }; -
pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
r184 r197 216 216 /* Done. */ 217 217 218 PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered", 219 (int)mod->name.slen, mod->name.ptr)); 220 218 221 on_return: 219 222 pj_rwmutex_unlock_write(endpt->mod_mutex); … … 254 257 } 255 258 259 /* Module MUST NOT set module ID to -1. */ 260 pj_assert(mod->id >= 0); 261 256 262 /* Remove module from array. */ 257 263 endpt->modules[mod->id] = NULL; … … 265 271 /* Done. */ 266 272 status = PJ_SUCCESS; 273 274 PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered", 275 (int)mod->name.slen, mod->name.ptr)); 267 276 268 277 on_return: -
pjproject/trunk/pjsip/src/pjsip/sip_errno.c
r184 r197 67 67 /* Transaction errors */ 68 68 { PJSIP_ETSXDESTROYED, "Transaction has been destroyed"}, 69 { PJSIP_ENOTSX, "No transaction (expecting stateful processing)" }, 69 70 70 71 /* Authentication. */ -
pjproject/trunk/pjsip/src/pjsip/sip_msg.c
r145 r197 146 146 pj_strset2( &status_phrase[487], "Request Terminated"); 147 147 pj_strset2( &status_phrase[488], "Not Acceptable Here"); 148 pj_strset2( &status_phrase[489], " UnknownEvent");148 pj_strset2( &status_phrase[489], "Bad Event"); 149 149 pj_strset2( &status_phrase[490], "Request Updated"); 150 150 pj_strset2( &status_phrase[491], "Request Pending"); -
pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c
r184 r197 522 522 } 523 523 524 /* Dialog MUST be found! */ 524 /* Dialog may not be found, e.g. in this case: 525 * - UAC sends SUBSCRIBE, then UAS sends NOTIFY before answering 526 * SUBSCRIBE request with 2xx. 527 * 528 * In this case, we can accept the request ONLY when the original 529 * dialog still has empty To tag. 530 */ 525 531 if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { 526 532 527 /* Not found. Mulfunction UAC? */ 528 pj_mutex_unlock(mod_ua.mutex); 529 530 PJ_LOG(5,(THIS_FILE, 531 "Unable to find dialog for %s, answering with 481", 532 pjsip_rx_data_get_info(rdata))); 533 534 pjsip_endpt_respond_stateless(mod_ua.endpt, rdata, 535 PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, 536 NULL, NULL, NULL); 537 return PJ_TRUE; 533 pjsip_dialog *first_dlg = dlg_set->dlg_list.next; 534 535 if (first_dlg->remote.info->tag.slen != 0) { 536 /* Not found. Mulfunction UAC? */ 537 pj_mutex_unlock(mod_ua.mutex); 538 539 PJ_LOG(5,(THIS_FILE, 540 "Unable to find dialog for %s, answering with 481", 541 pjsip_rx_data_get_info(rdata))); 542 543 pjsip_endpt_respond_stateless(mod_ua.endpt, rdata, 544 PJSIP_SC_CALL_TSX_DOES_NOT_EXIST, 545 NULL, NULL, NULL); 546 return PJ_TRUE; 547 } 548 549 dlg = first_dlg; 538 550 } 539 551 -
pjproject/trunk/pjsip/src/pjsua/main.c
r184 r197 23 23 #define THIS_FILE "main.c" 24 24 25 static pjsip_inv_session *inv_session; 25 /* Current dialog */ 26 static struct pjsua_inv_data *inv_session; 26 27 27 28 /* … … 36 37 37 38 if (inv->state == PJSIP_INV_STATE_DISCONNECTED) { 38 if (inv == inv_session) 39 inv_session = NULL; 39 if (inv == inv_session->inv) { 40 inv_session = inv_session->next; 41 if (inv_session == &pjsua.inv_list) 42 inv_session = pjsua.inv_list.next; 43 } 40 44 41 45 } else { 42 46 43 inv_session = inv; 47 if (inv_session == &pjsua.inv_list || inv_session == NULL) 48 inv_session = inv->mod_data[pjsua.mod.id]; 44 49 45 50 } … … 57 62 58 63 64 /* 65 * Print buddy list. 66 */ 67 static void print_buddy_list(void) 68 { 69 unsigned i; 70 71 puts("Buddy list:"); 72 //puts("-------------------------------------------------------------------------------"); 73 if (pjsua.buddy_cnt == 0) 74 puts(" -none-"); 75 else { 76 for (i=0; i<pjsua.buddy_cnt; ++i) { 77 const char *status; 78 79 if (pjsua.buddies[i].sub == NULL || 80 pjsua.buddies[i].status.info_cnt==0) 81 { 82 status = " ? "; 83 } 84 else if (pjsua.buddies[i].status.info[0].basic_open) 85 status = " Online"; 86 else 87 status = "Offline"; 88 89 printf(" [%2d] <%s> %s\n", 90 i+1, status, pjsua.buddies[i].uri.ptr); 91 } 92 } 93 puts(""); 94 } 59 95 60 96 /* 61 97 * Show a bit of help. 62 98 */ 63 static void ui_help(void) 64 { 65 puts(""); 66 puts("Console keys:"); 67 puts(" m Make a call/another call"); 68 puts(" d Dump application states"); 69 puts(" a Answer incoming call"); 70 puts(" h Hangup current call"); 71 puts(" q Quit"); 72 puts(""); 99 static void keystroke_help(void) 100 { 101 102 printf(">>>>\nOnline status: %s\n", 103 (pjsua.online_status ? "Online" : "Invisible")); 104 print_buddy_list(); 105 106 //puts("Commands:"); 107 puts("+=============================================================================+"); 108 puts("| Call Commands: | IM & Presence: | Misc: |"); 109 puts("| | | |"); 110 puts("| m Make new call | i Send IM | o Send OPTIONS |"); 111 puts("| a Answer call | s Subscribe presence | d Dump status |"); 112 puts("| h Hangup call | u Unsubscribe presence | d1 Dump detailed |"); 113 puts("| ] Select next dialog | t Toggle Online status | |"); 114 puts("| [ Select previous dialog | | |"); 115 puts("+-----------------------------------------------------------------------------+"); 116 puts("| q QUIT |"); 117 puts("+=============================================================================+"); 118 printf(">>> "); 119 120 73 121 fflush(stdout); 74 122 } 75 123 76 static pj_bool_t input(const char *title, char *buf, pj_size_t len) 124 125 /* 126 * Input simple string 127 */ 128 static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len) 77 129 { 78 130 char *p; … … 93 145 } 94 146 147 148 #define NO_NB -2 149 struct input_result 150 { 151 int nb_result; 152 char *uri_result; 153 }; 154 155 156 /* 157 * Input URL. 158 */ 159 static void ui_input_url(const char *title, char *buf, int len, 160 struct input_result *result) 161 { 162 result->nb_result = NO_NB; 163 result->uri_result = NULL; 164 165 print_buddy_list(); 166 167 printf("Choices:\n" 168 " 0 For current dialog.\n" 169 " -1 All %d buddies in buddy list\n" 170 " [1 -%2d] Select from buddy list\n" 171 " URL An URL\n" 172 " <Enter> Empty input (or 'q') to cancel\n" 173 , pjsua.buddy_cnt, pjsua.buddy_cnt); 174 printf("%s: ", title); 175 176 fflush(stdout); 177 fgets(buf, len, stdin); 178 len = strlen(buf); 179 180 /* Left trim */ 181 while (isspace(*buf)) { 182 ++buf; 183 --len; 184 } 185 186 /* Remove trailing newlines */ 187 while (len && (buf[len-1] == '\r' || buf[len-1] == '\n')) 188 buf[--len] = '\0'; 189 190 if (len == 0 || buf[0]=='q') 191 return; 192 193 if (isdigit(*buf) || *buf=='-') { 194 195 int i; 196 197 if (*buf=='-') 198 i = 1; 199 else 200 i = 0; 201 202 for (; i<len; ++i) { 203 if (!isdigit(buf[i])) { 204 puts("Invalid input"); 205 return; 206 } 207 } 208 209 result->nb_result = atoi(buf); 210 211 if (result->nb_result > 0 && result->nb_result <= (int)pjsua.buddy_cnt) { 212 --result->nb_result; 213 return; 214 } 215 if (result->nb_result == -1) 216 return; 217 218 puts("Invalid input"); 219 result->nb_result = NO_NB; 220 return; 221 222 } else { 223 pj_status_t status; 224 225 if ((status=pjsua_verify_sip_url(buf)) != PJ_SUCCESS) { 226 pjsua_perror("Invalid URL", status); 227 return; 228 } 229 230 result->uri_result = buf; 231 } 232 } 233 95 234 static void ui_console_main(void) 96 235 { 236 char menuin[10]; 97 237 char buf[128]; 98 238 pjsip_inv_session *inv; 99 100 //ui_help(); 239 struct input_result result; 240 241 //keystroke_help(); 101 242 102 243 for (;;) { 103 244 104 ui_help();105 fgets( buf, sizeof(buf), stdin);106 107 switch ( buf[0]) {245 keystroke_help(); 246 fgets(menuin, sizeof(menuin), stdin); 247 248 switch (menuin[0]) { 108 249 109 250 case 'm': 110 if (inv_session != NULL) {111 puts("Can not make call while another one is in progress");112 fflush(stdout);113 continue;114 }115 116 #if 1117 251 /* Make call! : */ 118 if ( !input("Enter URL to call", buf, sizeof(buf)))119 continue;120 pjsua_invite(buf, &inv);121 122 #else 123 124 pjsua_invite("sip:localhost:5061", &inv);125 #endif 126 break;127 128 129 case 'd':130 pjsua_dump();131 break; 252 if (pj_list_size(&pjsua.inv_list)) 253 printf("(You have %d calls)\n", pj_list_size(&pjsua.inv_list)); 254 255 ui_input_url("Make call", buf, sizeof(buf), &result); 256 if (result.nb_result != NO_NB) { 257 if (result.nb_result == -1) 258 puts("You can't do that with make call!"); 259 else 260 pjsua_invite(pjsua.buddies[result.nb_result].uri.ptr, &inv); 261 } else if (result.uri_result) 262 pjsua_invite(result.uri_result, &inv); 263 264 break; 265 132 266 133 267 case 'a': 134 268 135 if (inv_session == NULL || inv_session->role != PJSIP_ROLE_UAS || 136 inv_session->state >= PJSIP_INV_STATE_CONNECTING) 269 if (inv_session == &pjsua.inv_list || 270 inv_session->inv->role != PJSIP_ROLE_UAS || 271 inv_session->inv->state >= PJSIP_INV_STATE_CONNECTING) 137 272 { 138 273 puts("No pending incoming call"); … … 144 279 pjsip_tx_data *tdata; 145 280 146 if (! input("Answer with code (100-699)", buf, sizeof(buf)))281 if (!simple_input("Answer with code (100-699)", buf, sizeof(buf))) 147 282 continue; 148 283 … … 150 285 continue; 151 286 152 status = pjsip_inv_answer(inv_session , atoi(buf), NULL, NULL,153 &tdata);287 status = pjsip_inv_answer(inv_session->inv, atoi(buf), 288 NULL, NULL, &tdata); 154 289 if (status == PJ_SUCCESS) 155 status = pjsip_inv_send_msg(inv_session , tdata, NULL);290 status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL); 156 291 157 292 if (status != PJ_SUCCESS) … … 161 296 break; 162 297 298 163 299 case 'h': 164 300 165 if (inv_session == NULL) {301 if (inv_session == &pjsua.inv_list) { 166 302 puts("No current call"); 167 303 fflush(stdout); … … 172 308 pjsip_tx_data *tdata; 173 309 174 status = pjsip_inv_end_session(inv_session , PJSIP_SC_DECLINE,175 NULL, &tdata);310 status = pjsip_inv_end_session(inv_session->inv, 311 PJSIP_SC_DECLINE, NULL, &tdata); 176 312 if (status != PJ_SUCCESS) { 177 313 pjsua_perror("Failed to create end session message", status); … … 179 315 } 180 316 181 status = pjsip_inv_send_msg(inv_session , tdata, NULL);317 status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL); 182 318 if (status != PJ_SUCCESS) { 183 319 pjsua_perror("Failed to send end session message", status); … … 185 321 } 186 322 } 187 323 break; 324 325 case ']': 326 inv_session = inv_session->next; 327 if (inv_session == &pjsua.inv_list) 328 inv_session = pjsua.inv_list.next; 329 break; 330 331 case '[': 332 inv_session = inv_session->prev; 333 if (inv_session == &pjsua.inv_list) 334 inv_session = pjsua.inv_list.prev; 335 break; 336 337 case 's': 338 case 'u': 339 ui_input_url("Subscribe presence of", buf, sizeof(buf), &result); 340 if (result.nb_result != NO_NB) { 341 if (result.nb_result == -1) { 342 unsigned i; 343 for (i=0; i<pjsua.buddy_cnt; ++i) 344 pjsua.buddies[i].monitor = (menuin[0]=='s'); 345 } else { 346 pjsua.buddies[result.nb_result].monitor = (menuin[0]=='s'); 347 } 348 349 pjsua_pres_refresh(); 350 351 } else if (result.uri_result) { 352 puts("Sorry, can only subscribe to buddy's presence, not arbitrary URL (for now)"); 353 } 354 355 break; 356 357 case 't': 358 pjsua.online_status = !pjsua.online_status; 359 pjsua_pres_refresh(); 360 break; 361 362 case 'd': 363 pjsua_dump(); 188 364 break; 189 365 … … 255 431 { 256 432 NULL, NULL, /* prev, next. */ 257 { "mod- console-msg-logger", 22 },/* Name. */433 { "mod-pjsua-log", 13 }, /* Name. */ 258 434 -1, /* Id */ 259 435 PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority */ … … 386 562 387 563 564 /* No current call initially: */ 565 566 inv_session = &pjsua.inv_list; 567 568 388 569 /* Start UI console main loop: */ 389 570 -
pjproject/trunk/pjsip/src/pjsua/pjsua.h
r184 r197 32 32 #include <pjsip_ua.h> 33 33 34 /* Include all PJSIP-SIMPLE headers */ 35 #include <pjsip_simple.h> 36 34 37 /* Include all PJLIB-UTIL headers. */ 35 38 #include <pjlib-util.h> … … 60 63 void *mod_data[PJSIP_MAX_MODULE]; 61 64 }; 65 66 67 /** 68 * Buddy data. 69 */ 70 struct pjsua_buddy 71 { 72 pj_str_t uri; /**< Buddy URI */ 73 pj_bool_t monitor; /**< Should we monitor? */ 74 pjsip_evsub *sub; /**< Buddy presence subscription */ 75 pjsip_pres_status status; /**< Buddy presence status. */ 76 }; 77 78 typedef struct pjsua_buddy pjsua_buddy; 79 80 81 /** 82 * Server presence subscription list head. 83 */ 84 struct pjsua_srv_pres 85 { 86 PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); 87 pjsip_evsub *sub; 88 char *remote; 89 }; 90 91 typedef struct pjsua_srv_pres pjsua_srv_pres; 62 92 63 93 … … 142 172 143 173 144 /* Buddy list: */ 174 /* SIMPLE and buddy status: */ 175 176 pj_bool_t online_status; /**< Out online status. */ 177 pjsua_srv_pres pres_srv_list; /**< Server subscription list. */ 145 178 146 179 unsigned buddy_cnt; 147 pj _str_tbuddies[PJSUA_MAX_BUDDIES];180 pjsua_buddy buddies[PJSUA_MAX_BUDDIES]; 148 181 }; 149 182 … … 236 269 237 270 271 /** 272 * Terminate all calls. 273 */ 274 void pjsua_inv_shutdown(void); 275 276 238 277 /***************************************************************************** 239 278 * PJSUA Client Registration API (defined in pjsua_reg.c). … … 252 291 */ 253 292 void pjsua_regc_update(pj_bool_t renew); 293 294 295 296 297 /***************************************************************************** 298 * PJSUA Presence (pjsua_pres.c) 299 */ 300 301 /** 302 * Init presence. 303 */ 304 pj_status_t pjsua_pres_init(); 305 306 /** 307 * Refresh both presence client and server subscriptions. 308 */ 309 void pjsua_pres_refresh(void); 310 311 /** 312 * Terminate all subscriptions 313 */ 314 void pjsua_pres_shutdown(void); 315 316 /** 317 * Dump presence subscriptions. 318 */ 319 void pjsua_pres_dump(void); 254 320 255 321 -
pjproject/trunk/pjsip/src/pjsua/pjsua_core.c
r184 r197 80 80 81 81 pj_list_init(&pjsua.inv_list); 82 83 /* Init server presence subscription list: */ 84 85 pj_list_init(&pjsua.pres_srv_list); 86 82 87 } 83 88 … … 392 397 393 398 394 static int PJ_THREAD_FUNC pjsua_ worker_thread(void *arg)399 static int PJ_THREAD_FUNC pjsua_poll(void *arg) 395 400 { 396 401 PJ_UNUSED_ARG(arg); 397 402 398 while (!pjsua.quit_flag){403 do { 399 404 pj_time_val timeout = { 0, 10 }; 400 405 pjsip_endpt_handle_events (pjsua.endpt, &timeout); 401 } 406 } while (!pjsua.quit_flag); 402 407 403 408 return 0; … … 436 441 437 442 438 /* Init PJSIP and all the modules: */443 /* Init PJSIP : */ 439 444 440 445 status = init_stack(); … … 444 449 return status; 445 450 } 451 452 453 /* Init core SIMPLE module : */ 454 455 pjsip_evsub_init_module(pjsua.endpt); 456 457 /* Init presence module: */ 458 459 pjsip_pres_init_module( pjsua.endpt, pjsip_evsub_instance()); 460 461 462 /* Init pjsua presence handler: */ 463 464 pjsua_pres_init(); 446 465 447 466 … … 610 629 611 630 for (i=0; i<pjsua.thread_cnt; ++i) { 612 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_ worker_thread,631 status = pj_thread_create( pjsua.pool, "pjsua", &pjsua_poll, 613 632 NULL, 0, 0, &pjsua.threads[i]); 614 633 if (status != PJ_SUCCESS) { … … 636 655 637 656 638 657 PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION)); 639 658 return PJ_SUCCESS; 640 659 } 641 660 661 662 /* Sleep with polling */ 663 static void busy_sleep(unsigned msec) 664 { 665 pj_time_val timeout, now; 666 667 pj_gettimeofday(&timeout); 668 timeout.msec += msec; 669 pj_time_val_normalize(&timeout); 670 671 do { 672 pjsua_poll(NULL); 673 pj_gettimeofday(&now); 674 } while (PJ_TIME_VAL_LT(now, timeout)); 675 } 642 676 643 677 /* … … 648 682 int i; 649 683 650 /* Unregister, if required: */651 if (pjsua.regc) {652 653 pjsua_regc_update(0);654 655 /* Wait for some time to allow unregistration to complete: */656 657 pj_thread_sleep(500);658 }659 660 684 /* Signal threads to quit: */ 661 662 685 pjsua.quit_flag = 1; 663 686 664 665 /* Shutdown pjmedia-codec: */666 667 pjmedia_codec_deinit();668 669 670 /* Destroy sound framework:671 * (this should be done in pjmedia_shutdown())672 */673 pj_snd_deinit();674 675 687 /* Wait worker threads to quit: */ 676 677 688 for (i=0; i<pjsua.thread_cnt; ++i) { 678 689 … … 684 695 } 685 696 697 698 /* Terminate all calls. */ 699 pjsua_inv_shutdown(); 700 701 /* Terminate all presence subscriptions. */ 702 pjsua_pres_shutdown(); 703 704 /* Unregister, if required: */ 705 if (pjsua.regc) { 706 pjsua_regc_update(0); 707 } 708 709 /* Wait for some time to allow unregistration to complete: */ 710 PJ_LOG(4,(THIS_FILE, "Shutting down...")); 711 busy_sleep(1000); 712 713 /* Shutdown pjmedia-codec: */ 714 pjmedia_codec_deinit(); 715 716 /* Destroy sound framework: 717 * (this should be done in pjmedia_shutdown()) 718 */ 719 pj_snd_deinit(); 720 686 721 /* Destroy endpoint. */ 687 722 -
pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c
r169 r197 81 81 inv_data->inv = inv; 82 82 dlg->mod_data[pjsua.mod.id] = inv_data; 83 inv->mod_data[pjsua.mod.id] = inv_data; 83 84 84 85 … … 222 223 inv_data->inv = inv; 223 224 dlg->mod_data[pjsua.mod.id] = inv_data; 225 inv->mod_data[pjsua.mod.id] = inv_data; 224 226 225 227 pj_list_push_back(&pjsua.inv_list, inv_data); … … 346 348 } 347 349 } 350 351 352 /* 353 * Terminate all calls. 354 */ 355 void pjsua_inv_shutdown() 356 { 357 struct pjsua_inv_data *inv_data, *next; 358 359 inv_data = pjsua.inv_list.next; 360 while (inv_data != &pjsua.inv_list) { 361 pjsip_tx_data *tdata; 362 363 next = inv_data->next; 364 365 if (pjsip_inv_end_session(inv_data->inv, 410, NULL, &tdata)==0) 366 pjsip_inv_send_msg(inv_data->inv, tdata, NULL); 367 368 inv_data = next; 369 } 370 } 371 -
pjproject/trunk/pjsip/src/pjsua/pjsua_opt.c
r184 r197 404 404 return -1; 405 405 } 406 pjsua.buddies[pjsua.buddy_cnt++] = pj_str(optarg);406 pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(optarg); 407 407 break; 408 408 } … … 511 511 pjsip_endpt_dump(pjsua.endpt, 1); 512 512 pjmedia_endpt_dump(pjsua.med_endpt); 513 pjsip_tsx_layer_dump(); 513 514 pjsip_ua_dump(); 514 515 … … 536 537 } 537 538 } 539 540 /* Dump presence status */ 541 pjsua_pres_dump(); 538 542 539 543 pj_log_set_decor(old_decor); … … 548 552 { 549 553 int argc = 3; 550 char *argv[] = { "pjsua", "--config-file", (char*)filename, NULL}; 551 554 char *argv[4] = { "pjsua", "--config-file", NULL, NULL}; 555 556 argv[3] = (char*)filename; 552 557 return pjsua_parse_args(argc, argv); 553 558 } … … 655 660 for (i=0; i<pjsua.buddy_cnt; ++i) { 656 661 pj_ansi_sprintf(line, "--add-buddy %.*s\n", 657 (int)pjsua.buddies[i]. slen,658 pjsua.buddies[i]. ptr);662 (int)pjsua.buddies[i].uri.slen, 663 pjsua.buddies[i].uri.ptr); 659 664 pj_strcat2(&cfg, line); 660 665 }
Note: See TracChangeset
for help on using the changeset viewer.