Changeset 197 for pjproject


Ignore:
Timestamp:
Feb 19, 2006 1:38:06 AM (19 years ago)
Author:
bennylp
Message:

Initial SIMPLE implementation

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  
    9494############################################################################### 
    9595 
     96Project: "pjsip_simple"=.\pjsip_simple.dsp - Package Owner=<4> 
     97 
     98Package=<5> 
     99{{{ 
     100}}} 
     101 
     102Package=<4> 
     103{{{ 
     104}}} 
     105 
     106############################################################################### 
     107 
    96108Project: "pjsip_ua"=.\pjsip_ua.dsp - Package Owner=<4> 
    97109 
     
    135147    Project_Dep_Name pjlib_util 
    136148    End Project Dependency 
     149    Begin Project Dependency 
     150    Project_Dep_Name pjsip_simple 
     151    End Project Dependency 
    137152}}} 
    138153 
  • pjproject/trunk/pjsip/build/pjsip_simple.dsp

    r65 r197  
    3333# PROP BASE Use_MFC 0 
    3434# 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" 
    3737# PROP BASE Target_Dir "" 
    3838# PROP Use_MFC 0 
    3939# 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" 
    4242# PROP Target_Dir "" 
    4343# 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 /c 
     44# 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 
    4545# ADD BASE RSC /l 0x409 /d "NDEBUG" 
    4646# ADD RSC /l 0x409 /d "NDEBUG" 
     
    5050LIB32=link.exe -lib 
    5151# 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" 
    5353 
    5454!ELSEIF  "$(CFG)" == "pjsip_simple - Win32 Debug" 
     
    5656# PROP BASE Use_MFC 0 
    5757# 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" 
    6060# PROP BASE Target_Dir "" 
    6161# PROP Use_MFC 0 
    6262# 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" 
    6565# PROP Target_Dir "" 
    6666# 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 /c 
     67# 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 
    6868# ADD BASE RSC /l 0x409 /d "_DEBUG" 
    6969# ADD RSC /l 0x409 /d "_DEBUG" 
     
    7373LIB32=link.exe -lib 
    7474# 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" 
    7676 
    7777!ENDIF  
     
    8686# Begin Source File 
    8787 
    88 SOURCE=..\src\pjsip_simple\event_notify.c 
     88SOURCE="..\src\pjsip-simple\evsub.c" 
    8989# End Source File 
    9090# Begin Source File 
    9191 
    92 SOURCE=..\src\pjsip_simple\event_notify_msg.c 
     92SOURCE="..\src\pjsip-simple\evsub_msg.c" 
    9393# End Source File 
    9494# Begin Source File 
    9595 
    96 SOURCE=..\src\pjsip_simple\messaging.c 
     96SOURCE="..\src\pjsip-simple\pidf.c" 
    9797# End Source File 
    9898# Begin Source File 
    9999 
    100 SOURCE=..\src\pjsip_simple\pidf.c 
     100SOURCE="..\src\pjsip-simple\presence.c" 
    101101# End Source File 
    102102# Begin Source File 
    103103 
    104 SOURCE=..\src\pjsip_simple\presence.c 
    105 # End Source File 
    106 # Begin Source File 
    107  
    108 SOURCE=..\src\pjsip_simple\xpidf.c 
     104SOURCE="..\src\pjsip-simple\xpidf.c" 
    109105# End Source File 
    110106# End Group 
     
    114110# Begin Source File 
    115111 
    116 SOURCE=..\src\pjsip_simple\event_notify.h 
     112SOURCE="..\include\pjsip-simple\errno.h" 
    117113# End Source File 
    118114# Begin Source File 
    119115 
    120 SOURCE=..\src\pjsip_simple\event_notify_msg.h 
     116SOURCE="..\include\pjsip-simple\evsub.h" 
    121117# End Source File 
    122118# Begin Source File 
    123119 
    124 SOURCE=..\src\pjsip_simple\messaging.h 
     120SOURCE="..\include\pjsip-simple\evsub_msg.h" 
    125121# End Source File 
    126122# Begin Source File 
    127123 
    128 SOURCE=..\src\pjsip_simple\pidf.h 
     124SOURCE="..\include\pjsip-simple\pidf.h" 
    129125# End Source File 
    130126# Begin Source File 
    131127 
    132 SOURCE=..\src\pjsip_simple.h 
     128SOURCE=..\include\pjsip_simple.h 
    133129# End Source File 
    134130# Begin Source File 
    135131 
    136 SOURCE=..\src\pjsip_simple\presence.h 
     132SOURCE="..\include\pjsip-simple\presence.h" 
    137133# End Source File 
    138134# Begin Source File 
    139135 
    140 SOURCE=..\src\pjsip_simple\xpidf.h 
     136SOURCE="..\include\pjsip-simple\types.h" 
     137# End Source File 
     138# Begin Source File 
     139 
     140SOURCE="..\include\pjsip-simple\xpidf.h" 
    141141# End Source File 
    142142# End Group 
  • pjproject/trunk/pjsip/build/pjsip_ua.dsp

    r141 r197  
    3333# PROP BASE Use_MFC 0 
    3434# 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" 
    3737# PROP BASE Target_Dir "" 
    3838# PROP Use_MFC 0 
    3939# 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" 
    4242# PROP Target_Dir "" 
    4343# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c 
     
    5151LIB32=link.exe -lib 
    5252# 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" 
    5454 
    5555!ELSEIF  "$(CFG)" == "pjsip_ua - Win32 Debug" 
     
    5757# PROP BASE Use_MFC 0 
    5858# 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" 
    6161# PROP BASE Target_Dir "" 
    6262# PROP Use_MFC 0 
    6363# 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" 
    6666# PROP Target_Dir "" 
    6767# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 
     
    7575LIB32=link.exe -lib 
    7676# 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" 
    7878 
    7979!ENDIF  
     
    9393 
    9494SOURCE="..\src\pjsip-ua\sip_reg.c" 
    95  
    96 !IF  "$(CFG)" == "pjsip_ua - Win32 Release" 
    97  
    98 !ELSEIF  "$(CFG)" == "pjsip_ua - Win32 Debug" 
    99  
    100 !ENDIF  
    101  
    10295# End Source File 
    10396# End Group 
  • pjproject/trunk/pjsip/build/pjsua.dsp

    r184 r197  
    3333# PROP BASE Use_MFC 0 
    3434# 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" 
    3737# PROP BASE Target_Dir "" 
    3838# PROP Use_MFC 0 
    3939# 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" 
    4242# PROP Ignore_Export_Lib 0 
    4343# PROP Target_Dir "" 
     
    5959# PROP BASE Use_MFC 0 
    6060# 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" 
    6363# PROP BASE Target_Dir "" 
    6464# PROP Use_MFC 0 
    6565# 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" 
    6868# PROP Ignore_Export_Lib 0 
    6969# PROP Target_Dir "" 
     
    111111# Begin Source File 
    112112 
     113SOURCE=..\src\pjsua\pjsua_pres.c 
     114# End Source File 
     115# Begin Source File 
     116 
    113117SOURCE=..\src\pjsua\pjsua_reg.c 
    114118# End Source File 
  • pjproject/trunk/pjsip/include/pjsip-simple/evsub.h

    • Property svn:keywords set to id
    r187 r197  
    1515 * You should have received a copy of the GNU General Public License 
    1616 * 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_EVENT_NOTIFY_H__ 
    20 #define __PJSIP_SIMPLE_EVENT_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__ 
    2121 
    2222/** 
     
    2525 */ 
    2626 
    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 
    3129 
    3230/** 
     
    4543PJ_BEGIN_DECL 
    4644 
    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 */ 
     49typedef 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 */ 
     59enum pjsip_evsub_state 
    5860{ 
    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 */ 
     76typedef 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 */ 
     84struct pjsip_evsub_user 
    8485{ 
    8586    /** 
    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. 
    9892     * 
    9993     * @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. 
    118102     * 
    119103     * @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 
    126161     * the subscription. 
    127162     * 
     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     * 
    128167     * @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); 
    159179 
    160180}; 
    161181 
    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 */ 
     186typedef struct pjsip_evsub_user pjsip_evsub_user; 
     187 
     188 
     189/** 
     190 * SUBSCRIBE method constant. 
     191 */ 
     192extern const pjsip_method pjsip_subscribe_method; 
     193 
     194/** 
     195 * NOTIFY method constant. 
     196 */ 
     197extern 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 */ 
     210PJ_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 */ 
     219PJ_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. 
    214229 * @param accept_cnt    Number of strings in Accept array. 
    215230 * @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 */ 
     234PJ_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 */ 
     251PJ_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 */ 
     267PJ_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 */ 
     280PJ_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 */ 
     290PJ_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. 
    286296 * 
    287297 * @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 */ 
     309PJ_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 */ 
     326PJ_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. 
    297335 * 
    298336 * @param sub           The server subscription (notifier) instance. 
    299337 * @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. 
    300341 * @param reason        Specify reason if new state is terminated, otherwise 
    301342 *                      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 */ 
     347PJ_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 */ 
     363PJ_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 */ 
     376PJ_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 */ 
     389PJ_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 */ 
     399PJ_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 */ 
     411PJ_DECL(void*) pjsip_evsub_get_mod_data( pjsip_evsub *sub, unsigned mod_id ); 
     412 
    322413 
    323414 
     
    328419 */ 
    329420 
    330 #endif  /* __PJSIP_SIMPLE_EVENT_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  
    4242typedef struct pjsip_event_hdr 
    4343{ 
    44     PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr) 
     44    PJSIP_DECL_HDR_MEMBER(struct pjsip_event_hdr); 
    4545    pj_str_t        event_type;     /**< Event name. */ 
    4646    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. */ 
    4848} pjsip_event_hdr; 
    4949 
     
    6161 * This structure describes Allow-Events header. 
    6262 */ 
    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; 
     63typedef pjsip_generic_array_hdr pjsip_allow_events_hdr; 
    6964 
    7065 
     
    8479typedef struct pjsip_sub_state_hdr 
    8580{ 
    86     PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr) 
     81    PJSIP_DECL_HDR_MEMBER(struct pjsip_sub_state_hdr); 
    8782    pj_str_t        sub_state;          /**< Subscription state. */ 
    8883    pj_str_t        reason_param;       /**< Optional termination reason. */ 
    8984    int             expires_param;      /**< Expires param, or -1. */ 
    9085    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. */ 
    9287} pjsip_sub_state_hdr; 
    9388 
     
    10499 * Initialize parser for event notify module. 
    105100 */ 
    106 PJ_DEF(void) pjsip_event_notify_init_parser(void); 
     101PJ_DEF(void) pjsip_evsub_init_parser(void); 
    107102 
    108103 
  • pjproject/trunk/pjsip/include/pjsip-simple/pidf.h

    • Property svn:keywords set to id
    r65 r197  
    2424 * @brief PIDF/Presence Information Data Format (RFC 3863) 
    2525 */ 
    26 #include <pj/types.h> 
    27 #include <pj/xml.h> 
     26#include <pjsip-simple/types.h> 
     27#include <pjlib-util/xml.h> 
    2828 
    2929PJ_BEGIN_DECL 
  • pjproject/trunk/pjsip/include/pjsip-simple/presence.h

    • Property svn:keywords set to id
    r65 r197  
    2424 * @brief SIP Extension for Presence (RFC 3856) 
    2525 */ 
    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> 
    2929 
    3030 
     
    3939 * This module contains the implementation of SIP Presence Extension as  
    4040 * described in RFC 3856. It uses the SIP Event Notification framework 
    41  * (event_notify.h) and extends the framework by implementing "presence" 
     41 * (evsub.h) and extends the framework by implementing "presence" 
    4242 * event package. 
    4343 */ 
    4444 
    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 */ 
     58PJ_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 */ 
     67PJ_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 */ 
     75struct pjsip_pres_status 
    4976{ 
    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 */ 
     93typedef 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 */ 
     107PJ_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 */ 
     125PJ_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 */ 
     142PJ_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 */ 
     159PJ_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 */ 
     184PJ_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 */ 
     199PJ_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 */ 
     212PJ_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 */ 
     225PJ_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 */ 
     239PJ_DECL(pj_status_t) pjsip_pres_set_status( pjsip_evsub *sub, 
     240                                            const pjsip_pres_status *status ); 
    220241 
    221242 
  • pjproject/trunk/pjsip/include/pjsip-simple/xpidf.h

    • Property svn:keywords set to id
    r65 r197  
    2424 * @brief XPIDF/Presence Information Data Format 
    2525 */ 
    26 #include <pj/types.h> 
    27 #include <pj/xml.h> 
     26#include <pjsip-simple/types.h> 
     27#include <pjlib-util/xml.h> 
    2828 
    2929PJ_BEGIN_DECL 
  • pjproject/trunk/pjsip/include/pjsip/sip_config.h

    r127 r197  
    3434#define PJSIP_POOL_LEN_TRANSPORT        512 
    3535#define PJSIP_POOL_INC_TRANSPORT        512 
    36 #define PJSIP_POOL_LEN_TDATA            2500 
    37 #define PJSIP_POOL_INC_TDATA            512 
     36#define PJSIP_POOL_LEN_TDATA            4000 
     37#define PJSIP_POOL_INC_TDATA            4000 
    3838#define PJSIP_POOL_LEN_UA               (64 + 32*PJSIP_MAX_DIALOG_COUNT) 
    3939#define PJSIP_POOL_INC_UA               0 
     
    8080 
    8181/* Module related constants. */ 
    82 #define PJSIP_MAX_MODULE                8 
     82#define PJSIP_MAX_MODULE                16 
    8383 
    8484/* Maximum header types. */ 
  • pjproject/trunk/pjsip/include/pjsip/sip_errno.h

    r162 r197  
    208208 */ 
    209209#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 */ 
    210215 
    211216 
  • pjproject/trunk/pjsip/include/pjsip/sip_msg.h

    r145 r197  
    412412    PJSIP_SC_REQUEST_TERMINATED = 487, 
    413413    PJSIP_SC_NOT_ACCEPTABLE_HERE = 488, 
    414     PJSIP_SC_UNKNOWN_EVENT = 489, 
     414    PJSIP_SC_BAD_EVENT = 489, 
    415415    PJSIP_SC_REQUEST_UPDATED = 490, 
    416416    PJSIP_SC_REQUEST_PENDING = 491, 
     
    499499 * must fill in all members of this structure.  
    500500 */ 
    501 typedef struct pjsip_msg_body 
     501struct pjsip_msg_body 
    502502{ 
    503503    /** MIME content type.  
     
    566566    void* (*clone_data)(pj_pool_t *pool, const void *data, unsigned len); 
    567567 
    568 } pjsip_msg_body; 
     568}; 
    569569 
    570570/** 
  • pjproject/trunk/pjsip/include/pjsip/sip_types.h

    r171 r197  
    9393 
    9494/** 
     95 * Forward declaration for message body (sip_msg.h). 
     96 */ 
     97typedef struct pjsip_msg_body pjsip_msg_body; 
     98 
     99/** 
    95100 * Forward declaration for header field (sip_msg.h). 
    96101 */ 
     
    144149typedef enum pjsip_role_e 
    145150{ 
    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 
    148159} pjsip_role_e; 
    149160 
  • pjproject/trunk/pjsip/include/pjsip_simple.h

    • Property svn:keywords changed from Id to id
    r66 r197  
    3434#define __PJSIP_SIMPLE_H__ 
    3535 
    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> 
    4040 
    4141#endif  /* __PJSIP_SIMPLE_H__ */ 
  • pjproject/trunk/pjsip/src/pjsip-simple/evsub.c

    • Property svn:keywords set to id
    r187 r197  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    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> 
    2224#include <pjsip/sip_endpoint.h> 
    23 #include <pjsip/sip_module.h> 
     25#include <pjsip/sip_dialog.h> 
     26#include <pjsip/sip_auth.h> 
    2427#include <pjsip/sip_transaction.h> 
    2528#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> 
    2633#include <pj/pool.h> 
    27 #include <pj/timer.h> 
    2834#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 */ 
     46enum 
     47{ 
     48    PJSIP_SUBSCRIBE_METHOD = PJSIP_OTHER_METHOD, 
     49    PJSIP_NOTIFY_METHOD = PJSIP_OTHER_METHOD 
    4650}; 
    4751 
    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 
     52const pjsip_method pjsip_subscribe_method =  
     53{ 
     54    PJSIP_SUBSCRIBE_METHOD, 
     55    { "SUBSCRIBE", 9 } 
     56}; 
     57 
     58const pjsip_method pjsip_notify_method =  
     59{ 
     60    PJSIP_NOTIFY_METHOD, 
     61    { "NOTIFY", 6 } 
     62}; 
     63 
     64/* 
     65 * Static prototypes. 
     66 */ 
     67static void        mod_evsub_on_tsx_state(pjsip_transaction*, pjsip_event*); 
     68static pj_status_t mod_evsub_unload(void); 
     69 
     70 
     71/* 
     72 * State names. 
     73 */ 
     74static 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 */ 
     105enum 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 
     132static 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 */ 
     144struct 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 */ 
     158static 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 */ 
     190struct 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 */ 
     220struct dlgsub 
     221{ 
     222    PJ_DECL_LIST_MEMBER(struct dlgsub); 
     223    pjsip_evsub *sub; 
     224}; 
     225 
    59226 
    60227/* 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)); 
     228static const pj_str_t STR_EVENT      = { "Event", 5 }; 
     229static const pj_str_t STR_SUB_STATE  = { "Subscription-State", 18 }; 
     230static const pj_str_t STR_TERMINATED = { "terminated", 10 }; 
     231static const pj_str_t STR_ACTIVE     = { "active", 6 }; 
     232static const pj_str_t STR_PENDING    = { "pending", 7 }; 
     233static const pj_str_t STR_TIMEOUT    = { "timeout", 7}; 
     234 
     235/* 
     236 * On unload module. 
     237 */ 
     238static 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 */ 
     249PJ_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 
     280on_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 */ 
     293PJ_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 */ 
     304PJ_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 */ 
     313PJ_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 */ 
     324PJ_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 */ 
     334static 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 */ 
     354PJ_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; 
    195381    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 */ 
     414static 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 */ 
     424static 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 */ 
     455static 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 */ 
     484static 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 */ 
     518static 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 */ 
     607static 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; 
    408616    pj_status_t status; 
    409617 
    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 */ 
     687PJ_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 
     711on_return: 
     712    pjsip_dlg_dec_lock(dlg); 
    414713    return status; 
    415714} 
    416715 
    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 */ 
     720PJ_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 
     805on_return: 
     806    pjsip_dlg_dec_lock(dlg); 
     807    return status; 
     808} 
     809 
     810 
     811/* 
     812 * Get subscription state. 
     813 */ 
     814PJ_DEF(pjsip_evsub_state) pjsip_evsub_get_state(pjsip_evsub *sub) 
     815{ 
     816    return sub->state; 
     817} 
     818 
     819/* 
     820 * Get state name. 
     821 */ 
     822PJ_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 */ 
     831PJ_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) 
    481835{ 
    482836    pjsip_tx_data *tdata; 
    483837    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 
     880on_return: 
     881 
     882    pjsip_dlg_dec_lock(sub->dlg); 
     883    return status; 
     884} 
     885 
     886 
     887/* 
     888 * Accept incoming subscription request. 
     889 */ 
     890PJ_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 
     937on_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 */ 
     948static 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 */ 
     1000PJ_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 
     1050on_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 */ 
     1060PJ_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 */ 
     1071PJ_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)  
    4901095    { 
    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 
     1109on_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 */ 
     1119static 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 */ 
     1230static 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 */ 
     1277static 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 
    5381296    } 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 */ 
     1307static 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.  
    9311315         */ 
    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; 
    10631384                } 
    10641385            } 
    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 */ 
    12701432                return; 
    12711433            } 
    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 */ 
     1589static 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. 
    12901597         * 
    1291          * Note: 
    1292          * Since we normally send SUBSCRIBE for refreshing the subscription, 
    1293          * it means the subscription already expired anyway. So we terminate 
    1294          * the subscription now. 
    12951598         */ 
    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 
    13041731    } 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 
    13161733        /* 
    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! 
    13201735         */ 
    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 */ 
     1746static 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); 
    13751766    } 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) 
    14291778        { 
    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  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    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> 
    2121#include <pjsip/sip_parser.h> 
    2222#include <pj/pool.h> 
     
    2424#include <pj/except.h> 
    2525 
     26/* 
     27 * Event header. 
     28 */ 
    2629static int pjsip_event_hdr_print( pjsip_event_hdr *hdr,  
    2730                                  char *buf, pj_size_t size); 
     
    4144PJ_DEF(pjsip_event_hdr*) pjsip_event_hdr_create(pj_pool_t *pool) 
    4245{ 
    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)); 
    4547    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; 
    4752    hdr->vptr = &event_hdr_vptr; 
    4853    pj_list_init(hdr); 
     54    pj_list_init(&hdr->other_param); 
    4955    return hdr; 
    5056} 
     
    6369    copy_advance(p, hdr->event_type); 
    6470    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; 
    6779    return p - buf; 
    6880} 
     
    7486    pj_strdup(pool, &hdr->event_type, &rhs->event_type); 
    7587    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 
     92static pjsip_event_hdr*  
     93pjsip_event_hdr_shallow_clone( pj_pool_t *pool, 
     94                               const pjsip_event_hdr *rhs ) 
    8295{ 
    8396    pjsip_event_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); 
    8497    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 */ 
    106106PJ_DEF(pjsip_allow_events_hdr*) pjsip_allow_events_hdr_create(pj_pool_t *pool) 
    107107{ 
    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 */ 
    163125static int pjsip_sub_state_hdr_print(pjsip_sub_state_hdr *hdr,  
    164126                                     char *buf, pj_size_t size); 
     
    181143{ 
    182144    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)); 
    184146    hdr->type = PJSIP_H_OTHER; 
    185147    hdr->name = hdr->sname = sub_state; 
     
    188150    hdr->retry_after = -1; 
    189151    pj_list_init(hdr); 
     152    pj_list_init(&hdr->other_param); 
    190153    return hdr; 
    191154} 
     
    216179        p += printed; 
    217180    } 
    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; 
    220190 
    221191    return p - buf; 
     
    231201    hdr->retry_after = rhs->retry_after; 
    232202    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); 
    234204    return hdr; 
    235205} 
     
    241211    pjsip_sub_state_hdr *hdr = pj_pool_alloc(pool, sizeof(*hdr)); 
    242212    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 */ 
     221static pjsip_hdr *parse_hdr_event(pjsip_parse_ctx *ctx) 
     222{ 
     223    pjsip_event_hdr *hdr = pjsip_event_hdr_create(ctx->pool); 
    250224    const pj_str_t id_param = { "id", 2 }; 
    251225 
    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 == ';') { 
    255229        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 
    258234        if (pj_stricmp(&pname, &id_param)==0) { 
    259235            hdr->id_param = pvalue; 
    260236        } 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); 
    262241        } 
    263242    } 
    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 */ 
     250static pjsip_hdr* parse_hdr_sub_state( pjsip_parse_ctx *ctx ) 
     251{ 
     252    pjsip_sub_state_hdr *hdr = pjsip_sub_state_hdr_create(ctx->pool); 
    292253    const pj_str_t reason = { "reason", 6 }, 
    293254                   expires = { "expires", 7 }, 
    294255                   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 == ';') { 
    298259        pj_str_t pname, pvalue; 
    299260 
    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 
    302264        if (pj_stricmp(&pname, &reason) == 0) { 
    303265            hdr->reason_param = pvalue; 
     266 
    304267        } else if (pj_stricmp(&pname, &expires) == 0) { 
    305268            hdr->expires_param = pj_strtoul(&pvalue); 
     269 
    306270        } else if (pj_stricmp(&pname, &retry_after) == 0) { 
    307271            hdr->retry_after = pj_strtoul(&pvalue); 
     272 
    308273        } 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); 
    310278        } 
    311279    } 
    312280 
    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 */ 
     288PJ_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  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    19 #include <pjsip_simple/pidf.h> 
     19#include <pjsip-simple/pidf.h> 
    2020#include <pj/string.h> 
    2121#include <pj/pool.h> 
     22#include <pj/assert.h> 
     23 
    2224 
    2325struct pjpidf_op_desc pjpidf_op =  
     
    133135PJ_DEF(void) pjpidf_pres_remove_tuple(pjpidf_pres *pres, pjpidf_tuple *t) 
    134136{ 
    135     PJ_UNUSED_ARG(pres) 
     137    PJ_UNUSED_ARG(pres); 
    136138    pj_list_erase(t); 
    137139} 
  • pjproject/trunk/pjsip/src/pjsip-simple/presence.c

    • Property svn:keywords set to id
    r65 r197  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    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> 
    2129#include <pj/pool.h> 
    2230#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 */ 
     39static 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()                   */ 
    4255}; 
    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 */ 
     61typedef 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 */ 
     71struct 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.             */ 
    5279}; 
    5380 
    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 
     82typedef struct pjsip_pres pjsip_pres; 
     83 
     84 
     85/* 
     86 * Forward decl for evsub callback. 
     87 */ 
     88static void pres_on_evsub_state( pjsip_evsub *sub, pjsip_event *event); 
     89static void pres_on_evsub_tsx_state( pjsip_evsub *sub, pjsip_transaction *tsx, 
     90                                     pjsip_event *event); 
     91static 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); 
     97static 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); 
     103static void pres_on_evsub_client_refresh(pjsip_evsub *sub); 
     104static void pres_on_evsub_server_timeout(pjsip_evsub *sub); 
     105 
     106 
     107/* 
     108 * Event subscription callback for presence. 
     109 */ 
     110static 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 */ 
     124const pj_str_t STR_EVENT            = { "Event", 5 }; 
     125const pj_str_t STR_PRESENCE         = { "presence", 8 }; 
     126const pj_str_t STR_APPLICATION      = { "application", 11 }; 
     127const pj_str_t STR_PIDF_XML         = { "pidf+xml", 8}; 
     128const pj_str_t STR_XPIDF_XML        = { "xpidf+xml", 9}; 
     129const pj_str_t STR_APP_PIDF_XML     = { "application/pidf+xml", 20 }; 
     130const pj_str_t STR_APP_XPIDF_XML    = { "application/xpidf+xml", 21 }; 
     131 
     132 
     133/* 
     134 * Init presence module. 
     135 */ 
     136PJ_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 */ 
     172PJ_DEF(pjsip_module*) pjsip_pres_instance(void) 
     173{ 
     174    return &mod_presence; 
     175} 
     176 
     177 
     178/* 
     179 * Create client subscription. 
     180 */ 
     181PJ_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); 
    95192 
    96193    /* 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 
     209on_return: 
     210    pjsip_dlg_dec_lock(dlg); 
     211    return status; 
     212} 
     213 
     214 
     215/* 
     216 * Create server subscription. 
     217 */ 
     218PJ_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{ 
    293223    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); 
    302255    if (accept) { 
    303256        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; 
    364264                break; 
    365265            } 
    366266        } 
    367267 
    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 
     318on_return: 
     319    pjsip_dlg_dec_lock(dlg); 
     320    return status; 
     321} 
     322 
     323 
     324/* 
     325 * Create SUBSCRIBE 
     326 */ 
     327PJ_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 */ 
     339PJ_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 */ 
     351PJ_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 */ 
     373PJ_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); 
    392389        } else { 
    393             return; 
     390            pj_strdup(pres->dlg->pool,  
     391                      &pres->status.info[i].id, 
     392                      &status->info[i].id); 
    394393        } 
    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 */ 
     408static 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 */ 
     455static 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 */ 
     490static 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 */ 
     500static 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 */ 
     510static 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 */ 
     546PJ_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 
     586on_return: 
     587    pjsip_dlg_dec_lock(pres->dlg); 
     588    return status; 
     589} 
     590 
     591 
     592/* 
     593 * Create NOTIFY that reflect current state. 
     594 */ 
     595PJ_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 
     632on_return: 
     633    pjsip_dlg_dec_lock(pres->dlg); 
     634    return status; 
     635} 
     636 
     637 
     638/* 
     639 * Send request. 
     640 */ 
     641PJ_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 */ 
     652static 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 */ 
     666static 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 */ 
     682static 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 */ 
     719static 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 */ 
     764static 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 */ 
     791static 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 */ 
     892static 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 */ 
     914static 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  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    19 #include <pjsip_simple/xpidf.h> 
     19#include <pjsip-simple/xpidf.h> 
     20#include <pj/assert.h> 
     21#include <pj/guid.h> 
    2022#include <pj/pool.h> 
    2123#include <pj/string.h> 
    22 #include <pj/guid.h> 
    2324 
    2425static pj_str_t PRESENCE = { "presence", 8 }; 
  • pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c

    r184 r197  
    216216    /* Done. */ 
    217217 
     218    PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" registered",  
     219              (int)mod->name.slen, mod->name.ptr)); 
     220 
    218221on_return: 
    219222    pj_rwmutex_unlock_write(endpt->mod_mutex); 
     
    254257    } 
    255258 
     259    /* Module MUST NOT set module ID to -1. */ 
     260    pj_assert(mod->id >= 0); 
     261 
    256262    /* Remove module from array. */ 
    257263    endpt->modules[mod->id] = NULL; 
     
    265271    /* Done. */ 
    266272    status = PJ_SUCCESS; 
     273 
     274    PJ_LOG(4,(THIS_FILE, "Module \"%.*s\" unregistered",  
     275              (int)mod->name.slen, mod->name.ptr)); 
    267276 
    268277on_return: 
  • pjproject/trunk/pjsip/src/pjsip/sip_errno.c

    r184 r197  
    6767    /* Transaction errors */ 
    6868    { PJSIP_ETSXDESTROYED,      "Transaction has been destroyed"}, 
     69    { PJSIP_ENOTSX,             "No transaction (expecting stateful processing)" }, 
    6970 
    7071    /* Authentication. */ 
  • pjproject/trunk/pjsip/src/pjsip/sip_msg.c

    r145 r197  
    146146    pj_strset2( &status_phrase[487], "Request Terminated"); 
    147147    pj_strset2( &status_phrase[488], "Not Acceptable Here"); 
    148     pj_strset2( &status_phrase[489], "Unknown Event"); 
     148    pj_strset2( &status_phrase[489], "Bad Event"); 
    149149    pj_strset2( &status_phrase[490], "Request Updated"); 
    150150    pj_strset2( &status_phrase[491], "Request Pending"); 
  • pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c

    r184 r197  
    522522    } 
    523523 
    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     */ 
    525531    if (dlg == (pjsip_dialog*)&dlg_set->dlg_list) { 
    526532 
    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; 
    538550    } 
    539551 
  • pjproject/trunk/pjsip/src/pjsua/main.c

    r184 r197  
    2323#define THIS_FILE       "main.c" 
    2424 
    25 static pjsip_inv_session *inv_session; 
     25/* Current dialog */ 
     26static struct pjsua_inv_data *inv_session; 
    2627 
    2728/* 
     
    3637 
    3738    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        } 
    4044 
    4145    } else { 
    4246 
    43         inv_session = inv; 
     47        if (inv_session == &pjsua.inv_list || inv_session == NULL) 
     48            inv_session = inv->mod_data[pjsua.mod.id]; 
    4449 
    4550    } 
     
    5762 
    5863 
     64/* 
     65 * Print buddy list. 
     66 */ 
     67static 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} 
    5995 
    6096/* 
    6197 * Show a bit of help. 
    6298 */ 
    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(""); 
     99static 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 
    73121    fflush(stdout); 
    74122} 
    75123 
    76 static pj_bool_t input(const char *title, char *buf, pj_size_t len) 
     124 
     125/* 
     126 * Input simple string 
     127 */ 
     128static pj_bool_t simple_input(const char *title, char *buf, pj_size_t len) 
    77129{ 
    78130    char *p; 
     
    93145} 
    94146 
     147 
     148#define NO_NB   -2 
     149struct input_result 
     150{ 
     151    int   nb_result; 
     152    char *uri_result; 
     153}; 
     154 
     155 
     156/* 
     157 * Input URL. 
     158 */ 
     159static 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 
    95234static void ui_console_main(void) 
    96235{ 
     236    char menuin[10]; 
    97237    char buf[128]; 
    98238    pjsip_inv_session *inv; 
    99  
    100     //ui_help(); 
     239    struct input_result result; 
     240 
     241    //keystroke_help(); 
    101242 
    102243    for (;;) { 
    103244 
    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]) { 
    108249 
    109250        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 1 
    117251            /* 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 
    132266 
    133267        case 'a': 
    134268 
    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)  
    137272            { 
    138273                puts("No pending incoming call"); 
     
    144279                pjsip_tx_data *tdata; 
    145280 
    146                 if (!input("Answer with code (100-699)", buf, sizeof(buf))) 
     281                if (!simple_input("Answer with code (100-699)", buf, sizeof(buf))) 
    147282                    continue; 
    148283                 
     
    150285                    continue; 
    151286 
    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); 
    154289                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); 
    156291 
    157292                if (status != PJ_SUCCESS) 
     
    161296            break; 
    162297 
     298 
    163299        case 'h': 
    164300 
    165             if (inv_session == NULL) { 
     301            if (inv_session == &pjsua.inv_list) { 
    166302                puts("No current call"); 
    167303                fflush(stdout); 
     
    172308                pjsip_tx_data *tdata; 
    173309 
    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); 
    176312                if (status != PJ_SUCCESS) { 
    177313                    pjsua_perror("Failed to create end session message", status); 
     
    179315                } 
    180316 
    181                 status = pjsip_inv_send_msg(inv_session, tdata, NULL); 
     317                status = pjsip_inv_send_msg(inv_session->inv, tdata, NULL); 
    182318                if (status != PJ_SUCCESS) { 
    183319                    pjsua_perror("Failed to send end session message", status); 
     
    185321                } 
    186322            } 
    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(); 
    188364            break; 
    189365 
     
    255431{ 
    256432    NULL, NULL,                         /* prev, next.          */ 
    257     { "mod-console-msg-logger", 22 },   /* Name.                */ 
     433    { "mod-pjsua-log", 13 },            /* Name.                */ 
    258434    -1,                                 /* Id                   */ 
    259435    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */ 
     
    386562 
    387563 
     564    /* No current call initially: */ 
     565 
     566    inv_session = &pjsua.inv_list; 
     567 
     568 
    388569    /* Start UI console main loop: */ 
    389570 
  • pjproject/trunk/pjsip/src/pjsua/pjsua.h

    r184 r197  
    3232#include <pjsip_ua.h> 
    3333 
     34/* Include all PJSIP-SIMPLE headers */ 
     35#include <pjsip_simple.h> 
     36 
    3437/* Include all PJLIB-UTIL headers. */ 
    3538#include <pjlib-util.h> 
     
    6063    void                *mod_data[PJSIP_MAX_MODULE]; 
    6164}; 
     65 
     66 
     67/** 
     68 * Buddy data. 
     69 */ 
     70struct 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 
     78typedef struct pjsua_buddy pjsua_buddy; 
     79 
     80 
     81/** 
     82 * Server presence subscription list head. 
     83 */ 
     84struct pjsua_srv_pres 
     85{ 
     86    PJ_DECL_LIST_MEMBER(struct pjsua_srv_pres); 
     87    pjsip_evsub     *sub; 
     88    char            *remote; 
     89}; 
     90 
     91typedef struct pjsua_srv_pres pjsua_srv_pres; 
    6292 
    6393 
     
    142172 
    143173 
    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.      */ 
    145178 
    146179    unsigned        buddy_cnt; 
    147     pj_str_t        buddies[PJSUA_MAX_BUDDIES]; 
     180    pjsua_buddy     buddies[PJSUA_MAX_BUDDIES]; 
    148181}; 
    149182 
     
    236269 
    237270 
     271/** 
     272 * Terminate all calls. 
     273 */ 
     274void pjsua_inv_shutdown(void); 
     275 
     276 
    238277/***************************************************************************** 
    239278 * PJSUA Client Registration API (defined in pjsua_reg.c). 
     
    252291 */ 
    253292void 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 */ 
     304pj_status_t pjsua_pres_init(); 
     305 
     306/** 
     307 * Refresh both presence client and server subscriptions. 
     308 */ 
     309void pjsua_pres_refresh(void); 
     310 
     311/** 
     312 * Terminate all subscriptions 
     313 */ 
     314void pjsua_pres_shutdown(void); 
     315 
     316/** 
     317 * Dump presence subscriptions. 
     318 */ 
     319void pjsua_pres_dump(void); 
    254320 
    255321 
  • pjproject/trunk/pjsip/src/pjsua/pjsua_core.c

    r184 r197  
    8080 
    8181    pj_list_init(&pjsua.inv_list); 
     82 
     83    /* Init server presence subscription list: */ 
     84     
     85    pj_list_init(&pjsua.pres_srv_list); 
     86 
    8287} 
    8388 
     
    392397 
    393398 
    394 static int PJ_THREAD_FUNC pjsua_worker_thread(void *arg) 
     399static int PJ_THREAD_FUNC pjsua_poll(void *arg) 
    395400{ 
    396401    PJ_UNUSED_ARG(arg); 
    397402 
    398     while (!pjsua.quit_flag) { 
     403    do { 
    399404        pj_time_val timeout = { 0, 10 }; 
    400405        pjsip_endpt_handle_events (pjsua.endpt, &timeout); 
    401     } 
     406    } while (!pjsua.quit_flag); 
    402407 
    403408    return 0; 
     
    436441 
    437442 
    438     /* Init PJSIP and all the modules: */ 
     443    /* Init PJSIP : */ 
    439444 
    440445    status = init_stack(); 
     
    444449        return status; 
    445450    } 
     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(); 
    446465 
    447466 
     
    610629 
    611630    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, 
    613632                                   NULL, 0, 0, &pjsua.threads[i]); 
    614633        if (status != PJ_SUCCESS) { 
     
    636655 
    637656 
    638  
     657    PJ_LOG(3,(THIS_FILE, "PJSUA version %s started", PJ_VERSION)); 
    639658    return PJ_SUCCESS; 
    640659} 
    641660 
     661 
     662/* Sleep with polling */ 
     663static 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} 
    642676 
    643677/* 
     
    648682    int i; 
    649683 
    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  
    660684    /* Signal threads to quit: */ 
    661  
    662685    pjsua.quit_flag = 1; 
    663686 
    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  
    675687    /* Wait worker threads to quit: */ 
    676  
    677688    for (i=0; i<pjsua.thread_cnt; ++i) { 
    678689         
     
    684695    } 
    685696 
     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 
    686721    /* Destroy endpoint. */ 
    687722 
  • pjproject/trunk/pjsip/src/pjsua/pjsua_inv.c

    r169 r197  
    8181    inv_data->inv = inv; 
    8282    dlg->mod_data[pjsua.mod.id] = inv_data; 
     83    inv->mod_data[pjsua.mod.id] = inv_data; 
    8384 
    8485 
     
    222223            inv_data->inv = inv; 
    223224            dlg->mod_data[pjsua.mod.id] = inv_data; 
     225            inv->mod_data[pjsua.mod.id] = inv_data; 
    224226 
    225227            pj_list_push_back(&pjsua.inv_list, inv_data); 
     
    346348    } 
    347349} 
     350 
     351 
     352/* 
     353 * Terminate all calls. 
     354 */ 
     355void 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  
    404404                return -1; 
    405405            } 
    406             pjsua.buddies[pjsua.buddy_cnt++] = pj_str(optarg); 
     406            pjsua.buddies[pjsua.buddy_cnt++].uri = pj_str(optarg); 
    407407            break; 
    408408        } 
     
    511511    pjsip_endpt_dump(pjsua.endpt, 1); 
    512512    pjmedia_endpt_dump(pjsua.med_endpt); 
     513    pjsip_tsx_layer_dump(); 
    513514    pjsip_ua_dump(); 
    514515 
     
    536537        } 
    537538    } 
     539 
     540    /* Dump presence status */ 
     541    pjsua_pres_dump(); 
    538542 
    539543    pj_log_set_decor(old_decor); 
     
    548552{ 
    549553    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; 
    552557    return pjsua_parse_args(argc, argv); 
    553558} 
     
    655660    for (i=0; i<pjsua.buddy_cnt; ++i) { 
    656661        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); 
    659664        pj_strcat2(&cfg, line); 
    660665    } 
Note: See TracChangeset for help on using the changeset viewer.