Changeset 139 for pjproject


Ignore:
Timestamp:
Feb 7, 2006 12:34:11 PM (19 years ago)
Author:
bennylp
Message:

Initial implementation of invite session abstraction, and updated pjsua for the new framework

Location:
pjproject/trunk/pjsip
Files:
4 added
14 edited

Legend:

Unmodified
Added
Removed
  • pjproject/trunk/pjsip/build/pjsip_ua.dsp

    r127 r139  
    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 /W4 /Zi /O2 /I "../src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c 
     44# ADD CPP /nologo /MD /W4 /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c 
    4545# SUBTRACT CPP /YX 
    4646# ADD BASE RSC /l 0x409 /d "NDEBUG" 
     
    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 
    68 # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c 
     68# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c 
    6969# SUBTRACT CPP /YX 
    7070# ADD BASE RSC /l 0x409 /d "_DEBUG" 
     
    8888# Begin Source File 
    8989 
     90SOURCE="..\src\pjsip-ua\sip_inv.c" 
     91# End Source File 
     92# Begin Source File 
     93 
    9094SOURCE="..\src\pjsip-ua\sip_reg.c" 
    9195 
     
    105109# Begin Source File 
    106110 
     111SOURCE="..\include\pjsip-ua\sip_inv.h" 
     112# End Source File 
     113# Begin Source File 
     114 
    107115SOURCE="..\include\pjsip-ua\sip_regc.h" 
    108116# End Source File 
  • pjproject/trunk/pjsip/build/pjsua.dsp

    r65 r139  
    4343# PROP Target_Dir "" 
    4444# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 
    45 # ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../src" /I "../../pjlib/src" /I "../../pjmedia/src" /I "../../pjsdp/src" /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /c 
     45# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c 
    4646# SUBTRACT CPP /YX 
    4747# ADD BASE RSC /l 0x409 /d "NDEBUG" 
     
    6969# PROP Target_Dir "" 
    7070# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c 
    71 # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../src" /I "../../pjlib/src" /I "../../pjmedia/src" /I "../../pjsdp/src" /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c 
     71# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c 
    7272# SUBTRACT CPP /YX 
    7373# ADD BASE RSC /l 0x409 /d "_DEBUG" 
     
    9696 
    9797SOURCE=..\src\pjsua\main.c 
     98 
     99!IF  "$(CFG)" == "pjsua - Win32 Release" 
     100 
     101!ELSEIF  "$(CFG)" == "pjsua - Win32 Debug" 
     102 
     103# PROP Exclude_From_Build 1 
     104 
     105!ENDIF  
     106 
    98107# End Source File 
    99108# Begin Source File 
  • pjproject/trunk/pjsip/include/pjsip/sip_dialog.h

    r133 r139  
    6262    pj_mutex_t         *mutex;      /**< Dialog's mutex.                    */ 
    6363    pjsip_user_agent   *ua;         /**< User agent instance.               */ 
     64    pjsip_endpoint     *endpt;      /**< Endpoint instance.                 */ 
    6465 
    6566    /* The dialog set. */ 
  • pjproject/trunk/pjsip/include/pjsip/sip_event.h

    r112 r139  
    5858    PJSIP_EVENT_TSX_STATE, 
    5959 
    60     /** 2xx response received event. */ 
    61     PJSIP_EVENT_RX_200_MSG, 
    62  
    63     /** ACK request received event. */ 
    64     PJSIP_EVENT_RX_ACK_MSG, 
    65  
    66     /** Message discarded event. */ 
    67     PJSIP_EVENT_DISCARD_MSG, 
    68  
    6960    /** Indicates that the event was triggered by user action. */ 
    7061    PJSIP_EVENT_USER, 
    71  
    72     /** On before transmitting message. */ 
    73     PJSIP_EVENT_PRE_TX_MSG, 
    7462 
    7563} pjsip_event_id_e; 
     
    141129        } tx_msg; 
    142130 
    143         /** Pre-transmission event. */ 
    144         struct 
    145         { 
    146             pjsip_tx_data       *tdata; /**< Msg to be transmitted.     */ 
    147             pjsip_transaction   *tsx;   /**< The transaction.           */ 
    148             int                  retcnt;/**< Retransmission count.      */ 
    149         } pre_tx_msg; 
    150  
    151131        /** Transmission error event. */ 
    152132        struct 
     
    162142            pjsip_transaction   *tsx;   /**< The transaction.           */ 
    163143        } rx_msg; 
    164  
    165         /** Receipt of 200/INVITE response. */ 
    166         struct 
    167         { 
    168             pjsip_rx_data       *rdata; /**< The 200 response msg.      */ 
    169         } rx_200_msg; 
    170  
    171         /** Receipt of ACK message. */ 
    172         struct 
    173         { 
    174             pjsip_rx_data       *rdata; /**< The ack message.           */ 
    175         } rx_ack_msg; 
    176  
    177         /** Notification that endpoint has discarded a message. */ 
    178         struct 
    179         { 
    180             pjsip_rx_data       *rdata; /**< The discarded message.     */ 
    181             pj_status_t          reason;/**< The reason.                */ 
    182         } discard_msg; 
    183144 
    184145        /** User event. */ 
     
    246207 
    247208/** 
    248  * Init rx 200/INVITE event. 
    249  */ 
    250 #define PJSIP_EVENT_INIT_RX_200_MSG(event,prdata)       \ 
    251         do { \ 
    252             (event).type = PJSIP_EVENT_RX_200_MSG;      \ 
    253             (event).body.rx_200_msg.rdata = prdata;     \ 
    254         } while (0) 
    255  
    256 /** 
    257  * Init rx ack msg event. 
    258  */ 
    259 #define PJSIP_EVENT_INIT_RX_ACK_MSG(event,prdata)       \ 
    260         do { \ 
    261             (event).type = PJSIP_EVENT_RX_ACK_MSG;      \ 
    262             (event).body.rx_ack_msg.rdata = prdata;     \ 
    263         } while (0) 
    264  
    265 /** 
    266  * Init discard msg event. 
    267  */ 
    268 #define PJSIP_EVENT_INIT_DISCARD_MSG(event,prdata,preason)  \ 
    269         do { \ 
    270             (event).type = PJSIP_EVENT_DISCARD_MSG;         \ 
    271             (event).body.discard_msg.rdata = prdata;        \ 
    272             (event).body.discard_msg.reason = preason;      \ 
    273         } while (0) 
    274  
    275 /** 
    276209 * Init user event. 
    277210 */ 
     
    286219 
    287220/** 
    288  * Init pre tx msg event. 
    289  */ 
    290 #define PJSIP_EVENT_INIT_PRE_TX_MSG(event,ptsx,ptdata,pretcnt) \ 
    291         do { \ 
    292             (event).type = PJSIP_EVENT_PRE_TX_MSG;      \ 
    293             (event).body.pre_tx_msg.tsx = ptsx;         \ 
    294             (event).body.pre_tx_msg.tdata = ptdata;     \ 
    295             (event).body.pre_tx_msg.retcnt = pretcnt;   \ 
    296         } while (0) 
    297  
    298  
    299 /** 
    300221 * Get the event string from the event ID. 
    301222 * @param e the event ID. 
  • pjproject/trunk/pjsip/include/pjsip/sip_msg.h

    r127 r139  
    18221822 */ 
    18231823 
     1824 
     1825/////////////////////////////////////////////////////////////////////////////// 
     1826/** 
     1827 * @defgroup PJSIP_MSG_HDR_WARNING Header Field: Warning 
     1828 * @brief Warning header field. 
     1829 * @ingroup PJSIP_MSG 
     1830 * @{ 
     1831 */ 
     1832/** 
     1833 * SIP Warning header. 
     1834 * In this version, Warning header is just a typedef for generic string  
     1835 * header. 
     1836 */ 
     1837typedef pjsip_generic_string_hdr pjsip_warning_hdr; 
     1838 
     1839/** 
     1840 * Create a warning header with the specified contents. 
     1841 * 
     1842 * @param pool      Pool to allocate memory from. 
     1843 * @param code      Warning code, 300-399. 
     1844 * @param host      The host portion of the Warning header. 
     1845 * @param text      The warning text, which MUST not be quoted with 
     1846 *                  double quote. 
     1847 * 
     1848 * @return          The Warning header field. 
     1849 */ 
     1850PJ_DECL(pjsip_warning_hdr*) pjsip_warning_hdr_create( pj_pool_t *pool, 
     1851                                                      int code, 
     1852                                                      const pj_str_t *host, 
     1853                                                      const pj_str_t *text); 
     1854 
     1855/** 
     1856 * Create a warning header and initialize the contents from the error 
     1857 * message for the specified status code. The warning code will be 
     1858 * set to 399. 
     1859 * 
     1860 * @param pool      Pool to allocate memory from. 
     1861 * @param host      The host portion of the Warning header. 
     1862 * @param status    The error status code, which error text will be 
     1863 *                  put in as the Warning text. 
     1864 * 
     1865 * @return          The Warning header field. 
     1866 */ 
     1867PJ_DECL(pjsip_warning_hdr*)  
     1868pjsip_warning_hdr_create_from_status( pj_pool_t *pool, 
     1869                                      const pj_str_t *host, 
     1870                                      pj_status_t status); 
     1871 
     1872/** 
     1873 * @} 
     1874 */ 
     1875 
    18241876/** 
    18251877 * @bug Once a header is put in the message, the header CAN NOT be put in 
     
    19542006#define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create 
    19552007 
    1956 /** Warning header. */ 
    1957 typedef pjsip_generic_string_hdr pjsip_warning_hdr; 
    1958  
    1959 /** Create Warning header. */ 
    1960 #define pjsip_warning_hdr_create pjsip_generic_string_hdr_create 
    19612008 
    19622009/** 
  • pjproject/trunk/pjsip/include/pjsip_ua.h

    r66 r139  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    19  
    2019#ifndef __PJSIP_UA_H__ 
    2120#define __PJSIP_UA_H__ 
    2221 
    23 #include <pjsip_mod_ua/sip_dialog.h> 
    24 #include <pjsip_mod_ua/sip_reg.h> 
    25 #include <pjsip_mod_ua/sip_ua.h> 
     22#include <pjsip-ua/sip_inv.h> 
    2623 
    2724#endif  /* __PJSIP_UA_H__ */ 
  • pjproject/trunk/pjsip/src/pjsip/sip_dialog.c

    r132 r139  
    3838#define THIS_FILE       "sip_dialog.c" 
    3939 
     40long pjsip_dlg_lock_tls_id; 
    4041 
    4142PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m) 
     
    7374    pj_sprintf(dlg->obj_name, "dlg%p", dlg); 
    7475    dlg->ua = ua; 
     76    dlg->endpt = endpt; 
    7577 
    7678    status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex); 
     
    9395    if (dlg->mutex) 
    9496        pj_mutex_destroy(dlg->mutex); 
    95     pjsip_endpt_release_pool(pjsip_ua_get_endpt(dlg->ua), dlg->pool); 
     97    pjsip_endpt_release_pool(dlg->endpt, dlg->pool); 
    9698} 
    9799 
     
    145147    /* Randomize local CSeq. */ 
    146148    dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL; 
    147     dlg->local.cseq = dlg->local.cseq; 
     149    dlg->local.cseq = dlg->local.first_cseq; 
    148150 
    149151    /* Init local contact. */ 
     
    184186 
    185187    /* Init client authentication session. */ 
    186     status = pjsip_auth_clt_init(&dlg->auth_sess, pjsip_ua_get_endpt(ua),  
     188    status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt,  
    187189                                 dlg->pool, 0); 
    188190    if (status != PJ_SUCCESS) 
     
    343345 
    344346    /* Init client authentication session. */ 
    345     status = pjsip_auth_clt_init(&dlg->auth_sess, pjsip_ua_get_endpt(ua), 
     347    status = pjsip_auth_clt_init(&dlg->auth_sess, dlg->endpt, 
    346348                                 dlg->pool, 0); 
    347349    if (status != PJ_SUCCESS) 
     
    507509    } 
    508510 
     511    /* Log */ 
     512    PJ_LOG(5,(dlg->obj_name, "Dialog destroyed")); 
     513 
    509514    /* Destroy this dialog. */ 
    510515    pj_mutex_destroy(dlg->mutex); 
    511     pjsip_endpt_release_pool(pjsip_ua_get_endpt(dlg->ua), dlg->pool); 
     516    pjsip_endpt_release_pool(dlg->endpt, dlg->pool); 
    512517 
    513518    return PJ_SUCCESS; 
     
    630635    dlg->mod_data[mod->id] = mod_data; 
    631636 
     637    /* Increment count. */ 
     638    ++dlg->usage_cnt; 
     639 
    632640    pj_mutex_unlock(dlg->mutex); 
    633641 
     
    664672     * dialog. 
    665673     */ 
    666     status = pjsip_endpt_create_request_from_hdr(pjsip_ua_get_endpt(dlg->ua), 
     674    status = pjsip_endpt_create_request_from_hdr(dlg->endpt, 
    667675                                                 method, 
    668676                                                 dlg->target, 
     
    810818        } 
    811819 
    812         *p_tsx = tsx; 
     820        if (p_tsx) 
     821            *p_tsx = tsx; 
    813822 
    814823    } else { 
    815         status = pjsip_endpt_send_request_stateless( 
    816                     pjsip_ua_get_endpt(dlg->ua), tdata, NULL, NULL); 
     824        status = pjsip_endpt_send_request_stateless(dlg->endpt, tdata,  
     825                                                    NULL, NULL); 
    817826        if (status != PJ_SUCCESS) 
    818827            goto on_error; 
    819828 
    820         *p_tsx = NULL; 
     829        if (p_tsx) 
     830            *p_tsx = NULL; 
    821831    } 
    822832 
     
    833843    pjsip_tx_data_dec_ref( tdata ); 
    834844 
    835     *p_tsx = NULL; 
     845    if (p_tsx) 
     846        *p_tsx = NULL; 
    836847    return status; 
    837848} 
     
    853864 
    854865    /* Create generic response. */ 
    855     status = pjsip_endpt_create_response(pjsip_ua_get_endpt(dlg->ua), 
     866    status = pjsip_endpt_create_response(dlg->endpt, 
    856867                                         rdata, st_code, st_text, &tdata); 
    857868    if (status != PJ_SUCCESS) 
     
    915926        if (st_class==2 || st_code==405) { 
    916927            const pjsip_hdr *c_hdr; 
    917             c_hdr = pjsip_endpt_get_capability(pjsip_ua_get_endpt(dlg->ua), 
     928            c_hdr = pjsip_endpt_get_capability(dlg->endpt, 
    918929                                               PJSIP_H_ALLOW, NULL); 
    919930            if (c_hdr) { 
     
    926937        if (st_class==2) { 
    927938            const pjsip_hdr *c_hdr; 
    928             c_hdr = pjsip_endpt_get_capability(pjsip_ua_get_endpt(dlg->ua), 
     939            c_hdr = pjsip_endpt_get_capability(dlg->endpt, 
    929940                                               PJSIP_H_SUPPORTED, NULL); 
    930941            if (c_hdr) { 
     
    10571068         */ 
    10581069        pj_mutex_unlock(dlg->mutex); 
    1059         pjsip_endpt_respond_stateless(pjsip_ua_get_endpt(dlg->ua), 
     1070        pjsip_endpt_respond_stateless(dlg->endpt, 
    10601071                                      rdata, 500, NULL, NULL, NULL); 
    10611072        return; 
     
    10951106                  "Dialog will response with 500 (Internal Server Error)", 
    10961107                  pjsip_rx_data_get_info(rdata))); 
    1097         status = pjsip_endpt_create_response(pjsip_ua_get_endpt(dlg->ua),  
     1108        status = pjsip_endpt_create_response(dlg->endpt,  
    10981109                                             rdata,  
    10991110                                             PJSIP_SC_INTERNAL_SERVER_ERROR,  
     
    11261137    pj_mutex_lock(dlg->mutex); 
    11271138 
     1139    /* Check that rdata already has dialog in mod_data. */ 
     1140    pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); 
     1141 
    11281142    /* Update the remote tag, if none is specified yet. */ 
    11291143    if (dlg->remote.info->tag.slen == 0 && rdata->msg_info.to->tag.slen != 0) { 
     
    11341148    } 
    11351149 
    1136     /* Check that rdata already has dialog in mod_data. */ 
    1137     pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); 
     1150    /* Update remote target when receiving certain response messages. */ 
     1151    if (pjsip_method_creates_dialog(&rdata->msg_info.cseq->method) && 
     1152        rdata->msg_info.msg->line.status.code/100 == 2) 
     1153    { 
     1154        pjsip_contact_hdr *contact; 
     1155 
     1156        contact = pjsip_msg_find_hdr(rdata->msg_info.msg, PJSIP_H_CONTACT,  
     1157                                     NULL); 
     1158        if (contact) { 
     1159            dlg->remote.contact = pjsip_hdr_clone(dlg->pool, contact); 
     1160            dlg->target = dlg->remote.contact->uri; 
     1161        } 
     1162    } 
    11381163 
    11391164    /* Pass to dialog usages. */ 
     
    11861211        dlg->sess_count == 0)  
    11871212    { 
     1213        /* Unregister this dialog from the transaction. */ 
     1214        tsx->mod_data[dlg->ua->id] = NULL; 
     1215 
    11881216        /* Time to destroy dialog. */ 
    11891217        unregister_and_destroy_dialog(dlg); 
  • pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c

    r127 r139  
    311311        default: 
    312312            return PJ_EINVAL; 
     313        } 
     314 
     315        if (hdr) { 
     316            pj_list_push_back(&endpt->cap_hdr, hdr); 
    313317        } 
    314318    } 
     
    756760 
    757761    pj_rwmutex_unlock_read(endpt->mod_mutex); 
     762 
     763    /* Must clear mod_data before returning rdata to transport, since 
     764     * rdata may be reused. 
     765     */ 
     766    pj_memset(&rdata->endpt_info, 0, sizeof(rdata->endpt_info)); 
    758767} 
    759768 
  • pjproject/trunk/pjsip/src/pjsip/sip_msg.c

    r127 r139  
    17031703/////////////////////////////////////////////////////////////////////////////// 
    17041704/* 
     1705 * Warning header. 
     1706 */ 
     1707PJ_DEF(pjsip_warning_hdr*) pjsip_warning_hdr_create(  pj_pool_t *pool, 
     1708                                                      int code, 
     1709                                                      const pj_str_t *host, 
     1710                                                      const pj_str_t *text) 
     1711{ 
     1712    const pj_str_t str_warning = { "Warning", 7 }; 
     1713    pj_str_t hvalue; 
     1714 
     1715    hvalue.ptr = pj_pool_alloc(pool, 10 +               /* code */ 
     1716                                     host->slen + 2 +   /* host */ 
     1717                                     text->slen + 2);   /* text */ 
     1718    hvalue.slen = pj_sprintf(hvalue.ptr, "%u %.*s \"%.*s\"", 
     1719                             code, host->slen, host->ptr, 
     1720                             text->slen, text->ptr); 
     1721 
     1722    return pjsip_generic_string_hdr_create(pool, &str_warning, &hvalue); 
     1723} 
     1724 
     1725PJ_DEF(pjsip_warning_hdr*)  
     1726pjsip_warning_hdr_create_from_status( pj_pool_t *pool, 
     1727                                      const pj_str_t *host, 
     1728                                      pj_status_t status) 
     1729{ 
     1730    char errbuf[PJ_ERR_MSG_SIZE]; 
     1731    pj_str_t text; 
     1732     
     1733    text = pj_strerror(status, errbuf, sizeof(errbuf)); 
     1734    return pjsip_warning_hdr_create(pool, 399, host, &text); 
     1735} 
     1736 
     1737/////////////////////////////////////////////////////////////////////////////// 
     1738/* 
    17051739 * Message body manipulations. 
    17061740 */ 
  • pjproject/trunk/pjsip/src/pjsip/sip_transaction.c

    r130 r139  
    3232 
    3333#define THIS_FILE   "sip_transaction.c" 
     34 
     35#if 0 
     36#define TSX_TRACE_(expr)    PJ_LOG(3,expr) 
     37#else 
     38#define TSX_TRACE_(expr) 
     39#endif 
     40 
    3441 
    3542/***************************************************************************** 
     
    505512    pj_mutex_lock(mod_tsx_layer.mutex); 
    506513 
    507     /* Check if no transaction with the same key exists. */ 
    508     PJ_ASSERT_ON_FAIL(pj_hash_get( mod_tsx_layer.htable,  
    509                                    &tsx->transaction_key.ptr, 
    510                                    tsx->transaction_key.slen,  
    511                                    &tsx->hashed_key) == NULL, 
    512                         { 
    513                             pj_mutex_unlock(mod_tsx_layer.mutex); 
    514                             return PJ_EEXISTS; 
    515                         } 
    516                       ); 
     514    /* Check if no transaction with the same key exists.  
     515     * Do not use PJ_ASSERT_RETURN since it evaluates the expression 
     516     * twice! 
     517     */ 
     518    pj_assert(pj_hash_get( mod_tsx_layer.htable,  
     519                           &tsx->transaction_key.ptr, 
     520                           tsx->transaction_key.slen,  
     521                           NULL) == NULL); 
     522 
     523    TSX_TRACE_((THIS_FILE,  
     524                "Transaction %p registered with hkey=0x%p and key=%.*s", 
     525                tsx, tsx->hashed_key, tsx->transaction_key.slen, 
     526                tsx->transaction_key.ptr)); 
    517527 
    518528    /* Register the transaction to the hash table. */ 
     529    //pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, 
     530    //           tsx->transaction_key.slen, tsx->hashed_key, tsx); 
     531    PJ_TODO(USE_PRECALCULATED_HASHED_VALUE); 
    519532    pj_hash_set( tsx->pool, mod_tsx_layer.htable, tsx->transaction_key.ptr, 
    520                  tsx->transaction_key.slen, tsx->hashed_key, tsx); 
     533                 tsx->transaction_key.slen, 0, tsx); 
    521534 
    522535    /* Unlock mutex. */ 
     
    539552 
    540553    /* Register the transaction to the hash table. */ 
     554    //pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, 
     555    //           tsx->transaction_key.slen, tsx->hashed_key, NULL); 
     556    PJ_TODO(USE_PRECALCULATED_HASHED_VALUE); 
    541557    pj_hash_set( NULL, mod_tsx_layer.htable, tsx->transaction_key.ptr, 
    542                  tsx->transaction_key.slen, tsx->hashed_key, NULL); 
     558                 tsx->transaction_key.slen, 0, NULL); 
     559 
     560    TSX_TRACE_((THIS_FILE,  
     561                "Transaction %p unregistered, hkey=0x%p and key=%.*s", 
     562                tsx, tsx->hashed_key, tsx->transaction_key.slen, 
     563                tsx->transaction_key.ptr)); 
    543564 
    544565    /* Unlock mutex. */ 
     
    554575{ 
    555576    pjsip_transaction *tsx; 
     577    pj_uint32_t hval = 0; 
    556578 
    557579    pj_mutex_lock(mod_tsx_layer.mutex); 
    558     tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, NULL ); 
     580    tsx = pj_hash_get( mod_tsx_layer.htable, key->ptr, key->slen, &hval ); 
    559581    pj_mutex_unlock(mod_tsx_layer.mutex); 
    560582 
     583    TSX_TRACE_((THIS_FILE,  
     584                "Finding tsx with hkey=0x%p and key=%.*s: found %p", 
     585                hval, key->slen, key->ptr, tsx)); 
    561586 
    562587    /* Race condition! 
     
    642667{ 
    643668    pj_str_t key; 
     669    pj_uint32_t hval = 0; 
    644670    pjsip_transaction *tsx; 
    645671 
     
    650676    pj_mutex_lock( mod_tsx_layer.mutex ); 
    651677 
    652     tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, NULL ); 
     678    tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); 
     679 
     680 
     681    TSX_TRACE_((THIS_FILE,  
     682                "Finding tsx for request, hkey=0x%p and key=%.*s, found %p", 
     683                hval, key.slen, key.ptr, tsx)); 
     684 
    653685 
    654686    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { 
     
    683715{ 
    684716    pj_str_t key; 
     717    pj_uint32_t hval = 0; 
    685718    pjsip_transaction *tsx; 
    686719 
     
    691724    pj_mutex_lock( mod_tsx_layer.mutex ); 
    692725 
    693     tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, NULL ); 
     726    tsx = pj_hash_get( mod_tsx_layer.htable, key.ptr, key.slen, &hval ); 
     727 
     728 
     729    TSX_TRACE_((THIS_FILE,  
     730                "Finding tsx for response, hkey=0x%p and key=%.*s, found %p", 
     731                hval, key.slen, key.ptr, tsx)); 
     732 
    694733 
    695734    if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { 
     
    919958    } 
    920959 
    921     /* Inform TU */ 
     960    /* Before informing TU about state changed, inform TU about 
     961     * rx event. 
     962     */ 
     963    if (event_src_type==PJSIP_EVENT_RX_MSG && tsx->tsx_user) { 
     964        pjsip_rx_data *rdata = event_src; 
     965 
     966        pj_assert(rdata != NULL); 
     967 
     968        if (rdata->msg_info.msg->type == PJSIP_REQUEST_MSG && 
     969            tsx->tsx_user->on_rx_request) 
     970        { 
     971            (*tsx->tsx_user->on_rx_request)(rdata); 
     972 
     973        } else if (rdata->msg_info.msg->type == PJSIP_RESPONSE_MSG && 
     974                   tsx->tsx_user->on_rx_response) 
     975        { 
     976            (*tsx->tsx_user->on_rx_response)(rdata); 
     977        } 
     978 
     979    } 
     980 
     981    /* Inform TU about state changed. */ 
    922982    if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) { 
    923983        pjsip_event e; 
     
    10451105 
    10461106    /* Calculate hashed key value. */ 
     1107    PJ_TODO(OPTIMIZE_TSX_BY_PRECALCULATING_HASHED_KEY_VALUE); 
     1108    /* 
     1109     blp: somehow this yields different hashed value!! 
     1110 
    10471111    tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, 
    10481112                                   tsx->transaction_key.slen); 
     1113     */ 
    10491114 
    10501115    PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, 
     
    11591224 
    11601225    /* Calculate hashed key value. */ 
     1226    PJ_TODO(OPTIMIZE_TSX_BY_PRECALCULATING_HASHED_KEY_VALUE); 
     1227    /* 
     1228     blp: somehow this yields different hashed value!! 
     1229 
    11611230    tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, 
    11621231                                   tsx->transaction_key.slen); 
     1232     */ 
    11631233 
    11641234    /* Duplicate branch parameter for transaction. */ 
  • pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c

    r127 r139  
    169169    dlg = tsx->mod_data[mod_ua.mod.id]; 
    170170     
    171     /* Must have the dialog instance! */ 
    172     PJ_ASSERT_ON_FAIL(dlg != NULL, return); 
     171    /* If dialog instance has gone, it could mean that the dialog 
     172     * may has been destroyed. 
     173     */ 
     174    if (dlg == NULL) 
     175        return; 
    173176 
    174177    /* Hand over the event to the dialog. */ 
     
    205208{ 
    206209    return &mod_ua.mod; 
     210} 
     211 
     212 
     213/* 
     214 * Get the endpoint where this UA is currently registered. 
     215 */ 
     216PJ_DEF(pjsip_endpoint*) pjsip_ua_get_endpt(pjsip_user_agent *ua) 
     217{ 
     218    PJ_UNUSED_ARG(ua); 
     219    pj_assert(ua == &mod_ua.mod); 
     220    return mod_ua.endpt; 
    207221} 
    208222 
     
    461475    pjsip_dialog *dlg; 
    462476 
     477    /* Optimized path: bail out early if request doesn't have To tag */ 
     478    if (rdata->msg_info.to->tag.slen == 0) 
     479        return PJ_FALSE; 
     480 
    463481    /* Lock user agent before looking up the dialog hash table. */ 
    464482    pj_mutex_lock(mod_ua.mutex); 
     
    467485    dlg_set = find_dlg_set_for_msg(rdata); 
    468486 
    469     /* Bail out if dialog is not found. */ 
     487    /* If dialog is not found, respond with 481 (Call/Transaction 
     488     * Does Not Exist). 
     489     */ 
    470490    if (dlg_set == NULL) { 
    471         /* Not ours. */ 
     491        /* Unable to find dialog. */ 
    472492        pj_mutex_unlock(mod_ua.mutex); 
    473         return PJ_FALSE; 
     493 
     494        /* Respond with 481 . */ 
     495        pjsip_endpt_respond_stateless( mod_ua.endpt, rdata, 481, NULL, NULL, 
     496                                       NULL ); 
     497        return PJ_TRUE; 
    474498    } 
    475499 
     
    531555    /* Check if transaction is present. */ 
    532556    tsx = pjsip_rdata_get_tsx(rdata); 
    533     if (!tsx) { 
     557    if (tsx) { 
    534558        /* Check if dialog is present in the transaction. */ 
    535559        dlg = pjsip_tsx_get_dlg(tsx); 
  • pjproject/trunk/pjsip/src/pjsip/sip_util.c

    r131 r139  
    4444    "TRANSPORT_ERROR", 
    4545    "TSX_STATE", 
    46     "RX_2XX_RESPONSE", 
    47     "RX_ACK", 
    48     "DISCARD_MSG", 
    4946    "USER", 
    50     "BEFORE_TX", 
    5147}; 
    5248 
  • pjproject/trunk/pjsip/src/pjsua/getopt.c

    r65 r139  
    3636#include "config.h" 
    3737#endif 
     38 
     39#include <pj/string.h> 
    3840 
    3941#ifndef HAVE_GETOPT_LONG 
  • pjproject/trunk/pjsip/src/pjsua/main.c

    r119 r139  
    1717 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA  
    1818 */ 
    19 #include <pjlib.h> 
    20 #include <pjsip_core.h> 
    21 #include <pjsip_ua.h> 
    22 #include <pjsip_simple.h> 
    23 #include <pjmedia.h> 
    24 #include <ctype.h> 
    25 #include <stdlib.h> 
    26 #include <pj/stun.h> 
    27  
    28 #define START_PORT          5060 
    29 #define MAX_BUDDIES         32 
    30 #define THIS_FILE           "main.c" 
    31 #define MAX_PRESENTITY      32 
    32 #define PRESENCE_TIMEOUT    60 
    33  
    34 /* By default we'll have one worker thread, except when threading  
    35  * is disabled.  
     19#include "pjsua.h" 
     20 
     21/* For debugging, disable threading. */ 
     22//#define NO_WORKER_THREAD 
     23 
     24#ifdef NO_WORKER_THREAD 
     25#include <conio.h> 
     26#endif 
     27 
     28#define THIS_FILE       "main.c" 
     29 
     30static pjsip_inv_session *inv_session; 
     31 
     32/* 
     33 * Notify UI when invite state has changed. 
    3634 */ 
    37 #if PJ_HAS_THREADS 
    38 #  define WORKER_COUNT  1 
     35void ui_inv_on_state_changed(pjsip_inv_session *inv, pjsip_event *e) 
     36{ 
     37    const char *state_names[] = 
     38    { 
     39        "NULL", 
     40        "CALLING", 
     41        "INCOMING", 
     42        "EARLY", 
     43        "CONNECTING", 
     44        "CONFIRMED", 
     45        "DISCONNECTED", 
     46        "TERMINATED", 
     47    }; 
     48 
     49    PJ_UNUSED_ARG(e); 
     50 
     51    PJ_LOG(3,(THIS_FILE, "INVITE session state changed to %s", state_names[inv->state])); 
     52 
     53    if (inv->state == PJSIP_INV_STATE_DISCONNECTED || 
     54        inv->state == PJSIP_INV_STATE_TERMINATED) 
     55    { 
     56        if (inv == inv_session) 
     57            inv_session = NULL; 
     58 
     59    } else { 
     60 
     61        inv_session = inv; 
     62 
     63    } 
     64} 
     65 
     66static void ui_help(void) 
     67{ 
     68    puts(""); 
     69    puts("Console keys:"); 
     70    puts("  m    Make a call"); 
     71    puts("  h    Hangup current call"); 
     72    puts("  q    Quit"); 
     73    puts(""); 
     74} 
     75 
     76static void ui_console_main(void) 
     77{ 
     78    char keyin[10]; 
     79    char buf[128]; 
     80    char *p; 
     81    pjsip_inv_session *inv; 
     82 
     83    //ui_help(); 
     84 
     85    for (;;) { 
     86 
     87#ifdef NO_WORKER_THREAD 
     88        pj_time_val timeout = { 0, 10 }; 
     89        pjsip_endpt_handle_events (pjsua.endpt, &timeout); 
     90 
     91        if (kbhit()) 
     92            fgets(keyin, sizeof(keyin), stdin); 
    3993#else 
    40 #  define WORKER_COUNT  0 
    41 #endif 
    42  
    43 /* Global variable. */ 
    44 static struct 
    45 { 
    46     /* Control. */ 
    47     pj_pool_factory *pf; 
    48     pjsip_endpoint  *endpt; 
    49     pj_pool_t       *pool; 
    50     pjsip_user_agent *user_agent; 
    51     int              worker_cnt; 
    52     int              worker_quit_flag; 
    53  
    54     /* User info. */ 
    55     char             user_id[64]; 
    56     pj_str_t         local_uri; 
    57     pj_str_t         contact; 
    58     pj_str_t         real_contact; 
    59  
    60     /* Dialog. */ 
    61     pjsip_dlg       *cur_dlg; 
    62  
    63     /* Authentication. */ 
    64     int              cred_count; 
    65     pjsip_cred_info  cred_info[4]; 
    66  
    67     /* Media stack. */ 
    68     pj_bool_t        null_audio; 
    69     pj_med_mgr_t    *mmgr; 
    70  
    71     /* Misc. */ 
    72     int              app_log_level; 
    73     char            *log_filename; 
    74     FILE            *log_file; 
    75  
    76     /* Proxy URLs */ 
    77     pj_str_t         proxy; 
    78     pj_str_t         outbound_proxy; 
    79  
    80     /* UA auto options. */ 
    81     int              auto_answer;       /* -1 to disable. */ 
    82     int              auto_hangup;       /* -1 to disable */ 
    83  
    84     /* Registration. */ 
    85     pj_str_t         registrar_uri; 
    86     pjsip_regc      *regc; 
    87     pj_int32_t       reg_timeout; 
    88     pj_timer_entry   regc_timer; 
    89  
    90     /* STUN */ 
    91     pj_str_t         stun_srv1; 
    92     int              stun_port1; 
    93     pj_str_t         stun_srv2; 
    94     int              stun_port2; 
    95  
    96     /* UDP sockets and their public address. */ 
    97     int              sip_port; 
    98     pj_sock_t        sip_sock; 
    99     pj_sockaddr_in   sip_sock_name; 
    100     pj_sock_t        rtp_sock; 
    101     pj_sockaddr_in   rtp_sock_name; 
    102     pj_sock_t        rtcp_sock; 
    103     pj_sockaddr_in   rtcp_sock_name; 
    104  
    105     /* SIMPLE */ 
    106     pj_bool_t        hide_status; 
    107     pj_bool_t        offer_x_ms_msg; 
    108     int              im_counter; 
    109     int              buddy_cnt; 
    110     pj_str_t         buddy[MAX_BUDDIES]; 
    111     pj_bool_t        buddy_status[MAX_BUDDIES]; 
    112     pj_bool_t        no_presence; 
    113     pjsip_presentity *buddy_pres[MAX_BUDDIES]; 
    114  
    115     int             pres_cnt; 
    116     pjsip_presentity *pres[MAX_PRESENTITY]; 
    117  
    118 } global; 
    119  
    120 enum { AUTO_ANSWER, AUTO_HANGUP }; 
    121  
    122 /* This is the data that will be 'attached' on per dialog basis. */ 
    123 struct dialog_data 
    124 { 
    125     /* Media session. */ 
    126     pj_media_session_t *msession; 
    127  
    128     /* x-ms-chat session. */ 
    129     pj_bool_t           x_ms_msg_session; 
    130  
    131     /* Cached SDP body, updated when media session changed. */ 
    132     pjsip_msg_body *body; 
    133  
    134     /* Timer. */ 
    135     pj_bool_t        has_auto_timer; 
    136     pj_timer_entry   auto_timer; 
    137 }; 
    138  
    139 /* 
    140  * These are the callbacks to be registered to dialog to receive notifications 
    141  * about various events in the dialog. 
    142  */ 
    143 static void dlg_on_all_events   (pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, 
    144                                  pjsip_event *event ); 
    145 static void dlg_on_before_tx    (pjsip_dlg *dlg, pjsip_transaction *tsx,  
    146                                  pjsip_tx_data *tdata, int retransmission); 
    147 static void dlg_on_tx_msg       (pjsip_dlg *dlg, pjsip_transaction *tsx,  
    148                                  pjsip_tx_data *tdata); 
    149 static void dlg_on_rx_msg       (pjsip_dlg *dlg, pjsip_transaction *tsx,  
    150                                  pjsip_rx_data *rdata); 
    151 static void dlg_on_incoming     (pjsip_dlg *dlg, pjsip_transaction *tsx, 
    152                                  pjsip_rx_data *rdata); 
    153 static void dlg_on_calling      (pjsip_dlg *dlg, pjsip_transaction *tsx, 
    154                                  pjsip_tx_data *tdata); 
    155 static void dlg_on_provisional  (pjsip_dlg *dlg, pjsip_transaction *tsx, 
    156                                  pjsip_event *event); 
    157 static void dlg_on_connecting   (pjsip_dlg *dlg, pjsip_event *event); 
    158 static void dlg_on_established  (pjsip_dlg *dlg, pjsip_event *event); 
    159 static void dlg_on_disconnected (pjsip_dlg *dlg, pjsip_event *event); 
    160 static void dlg_on_terminated   (pjsip_dlg *dlg); 
    161 static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event); 
    162  
    163 /* The callback structure that will be registered to UA layer. */ 
    164 struct pjsip_dlg_callback dlg_callback = { 
    165     &dlg_on_all_events, 
    166     &dlg_on_before_tx, 
    167     &dlg_on_tx_msg, 
    168     &dlg_on_rx_msg, 
    169     &dlg_on_incoming, 
    170     &dlg_on_calling, 
    171     &dlg_on_provisional, 
    172     &dlg_on_connecting, 
    173     &dlg_on_established, 
    174     &dlg_on_disconnected, 
    175     &dlg_on_terminated, 
    176     &dlg_on_mid_call_evt 
    177 }; 
    178  
    179  
    180 /*  
    181  * Auxiliary things are put in misc.c, so that this main.c file is more  
    182  * readable.  
    183  */ 
    184 #include "misc.c" 
    185  
    186 static void dlg_auto_timer_callback( pj_timer_heap_t *timer_heap, 
    187                                      struct pj_timer_entry *entry) 
    188 { 
    189     pjsip_dlg *dlg = entry->user_data; 
    190     struct dialog_data *dlg_data = dlg->user_data; 
    191      
    192     PJ_UNUSED_ARG(timer_heap) 
    193  
    194     dlg_data->has_auto_timer = 0; 
    195  
    196     if (entry->id == AUTO_ANSWER) { 
    197         pjsip_tx_data *tdata = pjsip_dlg_answer(dlg, 200); 
    198         if (tdata) { 
    199             struct dialog_data *dlg_data = global.cur_dlg->user_data; 
    200             tdata->msg->body = dlg_data->body; 
    201             pjsip_dlg_send_msg(dlg, tdata); 
    202         } 
    203     } else { 
    204         pjsip_tx_data *tdata = pjsip_dlg_disconnect(dlg, 500); 
    205         if (tdata)  
    206             pjsip_dlg_send_msg(dlg, tdata); 
    207     } 
    208 } 
    209  
    210 static void update_registration(pjsip_regc *regc, int renew) 
    211 { 
    212     pjsip_tx_data *tdata; 
    213  
    214     PJ_LOG(3,(THIS_FILE, "Performing SIP registration...")); 
    215  
    216     if (renew) { 
    217         tdata = pjsip_regc_register(regc, 1); 
    218     } else { 
    219         tdata = pjsip_regc_unregister(regc); 
    220     } 
    221  
    222     pjsip_regc_send( regc, tdata ); 
    223 } 
    224  
    225 static void regc_cb(struct pjsip_regc_cbparam *param) 
    226 { 
    227     /* 
    228      * Print registration status. 
    229      */ 
    230     if (param->code < 0 || param->code >= 300) { 
    231         PJ_LOG(2, (THIS_FILE, "SIP registration failed, status=%d (%s)",  
    232                    param->code, pjsip_get_status_text(param->code)->ptr)); 
    233         global.regc = NULL; 
    234  
    235     } else if (PJSIP_IS_STATUS_IN_CLASS(param->code, 200)) { 
    236         PJ_LOG(3, (THIS_FILE, "SIP registration success, status=%d (%s), " 
    237                               "will re-register in %d seconds",  
    238                               param->code, 
    239                               pjsip_get_status_text(param->code)->ptr, 
    240                               param->expiration)); 
    241  
    242     } else { 
    243         PJ_LOG(4, (THIS_FILE, "SIP registration updated status=%d", param->code)); 
    244     } 
    245 } 
    246  
    247 static void pres_on_received_request(pjsip_presentity *pres, pjsip_rx_data *rdata, 
    248                                      int *timeout) 
    249 { 
    250     int state; 
    251     int i; 
    252     char url[PJSIP_MAX_URL_SIZE]; 
    253     int urllen; 
    254  
    255     PJ_UNUSED_ARG(rdata) 
    256  
    257     if (*timeout > 0) { 
    258         state = PJSIP_EVENT_SUB_STATE_ACTIVE; 
    259         if (*timeout > 300) 
    260             *timeout = 300; 
    261     } else { 
    262         state = PJSIP_EVENT_SUB_STATE_TERMINATED; 
    263     } 
    264  
    265     urllen = pjsip_uri_print(PJSIP_URI_IN_FROMTO_HDR, rdata->from->uri, url, sizeof(url)-1); 
    266     if (urllen < 1) { 
    267         pj_native_strcpy(url, "<unknown>"); 
    268     } else { 
    269         url[urllen] = '\0'; 
    270     } 
    271     PJ_LOG(3,(THIS_FILE, "Received presence request from %s, sub_state=%s",  
    272                          url,  
    273                          (state==PJSIP_EVENT_SUB_STATE_ACTIVE?"active":"terminated"))); 
    274  
    275     for (i=0; i<global.pres_cnt; ++i) 
    276         if (global.pres[i] == pres) 
     94        ui_help(); 
     95        fgets(keyin, sizeof(keyin), stdin); 
     96#endif 
     97 
     98        switch (keyin[0]) { 
     99 
     100        case 'm': 
     101            if (inv_session != NULL) { 
     102                puts("Can not make call while another one is in progress"); 
     103                continue; 
     104            } 
     105 
     106#if 0 
     107            printf("Enter URL to call: "); 
     108            fgets(buf, sizeof(buf), stdin); 
     109 
     110            if (buf[0]=='\r' || buf[0]=='\n') { 
     111                /* Cancelled. */ 
     112                puts("<cancelled>"); 
     113                continue; 
     114            } 
     115 
     116            /* Remove trailing newlines. */ 
     117            for (p=buf; ; ++p) { 
     118                if (*p=='\r' || *p=='\n') *p='\0'; 
     119                else if (!*p) break; 
     120            } 
     121            /* Make call! : */ 
     122 
     123            pjsua_invite(buf, &inv); 
     124#endif 
     125 
     126            pjsua_invite("sip:localhost:5061", &inv); 
    277127            break; 
    278     if (i == global.pres_cnt) 
    279         global.pres[global.pres_cnt++] = pres; 
    280  
    281     pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); 
    282     pjsip_presence_notify(pres, state, !global.hide_status); 
    283  
    284 } 
    285  
    286 static void pres_on_received_refresh(pjsip_presentity *pres, pjsip_rx_data *rdata) 
    287 { 
    288     pres_on_received_request(pres, rdata, &pres->sub->default_interval); 
    289 } 
    290  
    291 /* This is called by presence framework when we receives presence update 
    292  * of a resource (buddy). 
    293  */ 
    294 static void pres_on_received_update(pjsip_presentity *pres, pj_bool_t is_open) 
    295 { 
    296     int buddy_index = (int)pres->user_data; 
    297  
    298     global.buddy_status[buddy_index] = is_open; 
    299     PJ_LOG(3,(THIS_FILE, "Presence update: %s is %s",  
    300                          global.buddy[buddy_index].ptr, 
    301                          (is_open ? "Online" : "Offline"))); 
    302 } 
    303  
    304 /* This is called when the subscription is terminated. */ 
    305 static void pres_on_terminated(pjsip_presentity *pres, const pj_str_t *reason) 
    306 { 
    307     if (pres->sub->role == PJSIP_ROLE_UAC) { 
    308         int buddy_index = (int)pres->user_data; 
    309         PJ_LOG(3,(THIS_FILE, "Presence subscription for %s is terminated (reason=%.*s)",  
    310                             global.buddy[buddy_index].ptr, 
    311                             reason->slen, reason->ptr)); 
    312         global.buddy_pres[buddy_index] = NULL; 
    313         global.buddy_status[buddy_index] = 0; 
    314     } else { 
    315         int i; 
    316         PJ_LOG(3,(THIS_FILE, "Notifier terminated (reason=%.*s)",  
    317                             reason->slen, reason->ptr)); 
    318         pjsip_presence_notify(pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 1); 
    319         for (i=0; i<global.pres_cnt; ++i) { 
    320             if (global.pres[i] == pres) { 
    321                 int j; 
    322                 global.pres[i] = NULL; 
    323                 for (j=i+1; j<global.pres_cnt; ++j) 
    324                     global.pres[j-1] = global.pres[j]; 
    325                 global.pres_cnt--; 
    326                 break; 
    327             } 
     128 
     129 
     130        case 'h': 
     131 
     132            if (inv_session == NULL) { 
     133                puts("No current call"); 
     134                continue; 
     135 
     136            } else { 
     137                pj_status_t status; 
     138                pjsip_tx_data *tdata; 
     139 
     140                status = pjsip_inv_end_session(inv_session, PJSIP_SC_DECLINE,  
     141                                               NULL, &tdata); 
     142                if (status != PJ_SUCCESS) { 
     143                    pjsua_perror("Failed to create end session message", status); 
     144                    continue; 
     145                } 
     146 
     147                status = pjsip_inv_send_msg(inv_session, tdata, NULL); 
     148                if (status != PJ_SUCCESS) { 
     149                    pjsua_perror("Failed to send end session message", status); 
     150                    continue; 
     151                } 
     152            } 
     153 
     154            break; 
     155 
     156        case 'q': 
     157            goto on_exit; 
    328158        } 
    329159    } 
    330     pjsip_presence_destroy(pres); 
    331 } 
    332  
    333  
    334 /* Callback attached to SIP body to print the body to message buffer. */ 
    335 static int print_msg_body(pjsip_msg_body *msg_body, char *buf, pj_size_t size) 
    336 { 
    337     pjsip_msg_body *body = msg_body; 
    338     return pjsdp_print ((pjsdp_session_desc*)body->data, buf, size); 
    339 } 
    340  
    341 /* When media session has changed, call this function to update the cached body 
    342  * information in the dialog.  
    343  */ 
    344 static pjsip_msg_body *create_msg_body (pjsip_dlg *dlg, pj_bool_t is_ack_msg) 
    345 { 
    346     struct dialog_data *dlg_data = dlg->user_data; 
    347     pjsdp_session_desc *sdp; 
    348  
    349     sdp = pj_media_session_create_sdp (dlg_data->msession, dlg->pool, is_ack_msg); 
    350     if (!sdp) { 
    351         dlg_data->body = NULL; 
    352         return NULL; 
    353     } 
    354  
    355     /* For outgoing INVITE, if we offer "x-ms-message" line, then add a new 
    356      * "m=" line in the SDP. 
     160 
     161on_exit: 
     162    ; 
     163} 
     164 
     165static pj_bool_t console_on_rx_msg(pjsip_rx_data *rdata) 
     166{ 
     167    PJ_LOG(4,(THIS_FILE, "RX %d bytes %s from %s:%d:\n" 
     168                         "%s\n" 
     169                         "--end msg--", 
     170                         rdata->msg_info.len, 
     171                         pjsip_rx_data_get_info(rdata), 
     172                         rdata->pkt_info.src_name, 
     173                         rdata->pkt_info.src_port, 
     174                         rdata->msg_info.msg_buf)); 
     175     
     176    /* Must return false for logger! */ 
     177    return PJ_FALSE; 
     178} 
     179 
     180static pj_status_t console_on_tx_msg(pjsip_tx_data *tdata) 
     181{ 
     182    PJ_LOG(4,(THIS_FILE, "TX %d bytes %s to %s:%d:\n" 
     183                         "%s\n" 
     184                         "--end msg--", 
     185                         (tdata->buf.cur - tdata->buf.start), 
     186                         pjsip_tx_data_get_info(tdata), 
     187                         tdata->tp_info.dst_name, 
     188                         tdata->tp_info.dst_port, 
     189                         tdata->buf.start)); 
     190 
     191    return PJ_SUCCESS; 
     192} 
     193 
     194static pjsip_module console_msg_logger =  
     195{ 
     196    NULL, NULL,                         /* prev, next.          */ 
     197    { "mod-console-msg-logger", 22 },   /* Name.                */ 
     198    -1,                                 /* Id                   */ 
     199    PJSIP_MOD_PRIORITY_TRANSPORT_LAYER-1,/* Priority            */ 
     200    NULL,                               /* User data.           */ 
     201    NULL,                               /* load()               */ 
     202    NULL,                               /* start()              */ 
     203    NULL,                               /* stop()               */ 
     204    NULL,                               /* unload()             */ 
     205    &console_on_rx_msg,                 /* on_rx_request()      */ 
     206    &console_on_rx_msg,                 /* on_rx_response()     */ 
     207    &console_on_tx_msg,                 /* on_tx_request.       */ 
     208    &console_on_tx_msg,                 /* on_tx_response()     */ 
     209    NULL,                               /* on_tsx_state()       */ 
     210 
     211}; 
     212 
     213 
     214int main() 
     215{ 
     216    /* Init default settings. */ 
     217 
     218    pjsua_default(); 
     219 
     220 
     221#ifdef NO_WORKER_THREAD 
     222    pjsua.thread_cnt = 0; 
     223#endif 
     224 
     225 
     226    /* Initialize pjsua. 
     227     * This will start worker thread, client registration, etc. 
    357228     */ 
    358     if (dlg_data->x_ms_msg_session >= 0 &&  
    359         dlg_data->x_ms_msg_session >= (int)sdp->media_count)  
    360     { 
    361         pjsdp_media_desc *m = pj_pool_calloc(dlg->pool, 1, sizeof(*m)); 
    362         sdp->media[sdp->media_count] = m; 
    363         dlg_data->x_ms_msg_session = sdp->media_count++; 
    364     } 
    365  
    366     /* 
    367      * For "x-ms-message" line, remove all attributes and connection line etc. 
     229 
     230    if (pjsua_init() != PJ_SUCCESS) 
     231        return 1; 
     232 
     233    /* Register message logger to print incoming and outgoing 
     234     * messages. 
    368235     */ 
    369     if (dlg_data->x_ms_msg_session >= 0) { 
    370         pjsdp_media_desc *m = sdp->media[dlg_data->x_ms_msg_session]; 
    371         if (m) { 
    372             m->desc.media = pj_str("x-ms-message"); 
    373             m->desc.port = 5060; 
    374             m->desc.transport = pj_str("sip"); 
    375             m->desc.fmt_count = 1; 
    376             m->desc.fmt[0] = pj_str("null"); 
    377             m->attr_count = 0; 
    378             m->conn = NULL; 
    379         } 
    380     } 
    381  
    382     dlg_data->body = pj_pool_calloc(dlg->pool, 1, sizeof(*dlg_data->body)); 
    383     dlg_data->body->content_type.type = pj_str("application"); 
    384     dlg_data->body->content_type.subtype = pj_str("sdp"); 
    385     dlg_data->body->len = 0;    /* ignored */ 
    386     dlg_data->body->print_body = &print_msg_body; 
    387  
    388     dlg_data->body->data = sdp; 
    389     return dlg_data->body; 
    390 } 
    391  
    392 /* This callback will be called on every occurence of events in dialogs */ 
    393 static void dlg_on_all_events(pjsip_dlg *dlg, pjsip_dlg_event_e dlg_evt, 
    394                               pjsip_event *event ) 
    395 { 
    396     PJ_UNUSED_ARG(dlg_evt) 
    397     PJ_UNUSED_ARG(event) 
    398  
    399     PJ_LOG(4, (THIS_FILE, "dlg_on_all_events %p", dlg)); 
    400 } 
    401  
    402 /* This callback is called before each outgoing msg is sent (including  
    403  * retransmission). Application can override this notification if it wants 
    404  * to modify the message before transmission or if it wants to do something 
    405  * else for each transmission. 
    406  */ 
    407 static void dlg_on_before_tx(pjsip_dlg *dlg, pjsip_transaction *tsx,  
    408                              pjsip_tx_data *tdata, int ret_cnt) 
    409 { 
    410     PJ_UNUSED_ARG(tsx) 
    411     PJ_UNUSED_ARG(tdata) 
    412  
    413     if (ret_cnt > 0) { 
    414         PJ_LOG(3, (THIS_FILE, "Dialog %s: retransmitting message (cnt=%d)",  
    415                               dlg->obj_name, ret_cnt)); 
    416     } 
    417 } 
    418  
    419 /* This callback is called after a message is sent. */ 
    420 static void dlg_on_tx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,  
    421                           pjsip_tx_data *tdata) 
    422 { 
    423     PJ_UNUSED_ARG(tsx) 
    424     PJ_UNUSED_ARG(tdata) 
    425  
    426     PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); 
    427 } 
    428  
    429 /* This callback is called on receipt of incoming message. */ 
    430 static void dlg_on_rx_msg(pjsip_dlg *dlg, pjsip_transaction *tsx,  
    431                           pjsip_rx_data *rdata) 
    432 { 
    433     PJ_UNUSED_ARG(tsx) 
    434     PJ_UNUSED_ARG(rdata) 
    435     PJ_LOG(4, (THIS_FILE, "dlg_on_tx_msg %p", dlg)); 
    436 } 
    437  
    438 /* This callback is called after dialog has sent INVITE */ 
    439 static void dlg_on_calling(pjsip_dlg *dlg, pjsip_transaction *tsx, 
    440                            pjsip_tx_data *tdata) 
    441 { 
    442     PJ_UNUSED_ARG(tsx) 
    443     PJ_UNUSED_ARG(tdata) 
    444  
    445     pj_assert(tdata->msg->type == PJSIP_REQUEST_MSG && 
    446               tdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && 
    447               tsx->method.id == PJSIP_INVITE_METHOD); 
    448  
    449     PJ_LOG(3, (THIS_FILE, "Dialog %s: start calling...", dlg->obj_name)); 
    450 } 
    451  
    452 static void create_session_from_sdp( pjsip_dlg *dlg, pjsdp_session_desc *sdp) 
    453 { 
    454     struct dialog_data *dlg_data = dlg->user_data; 
    455     pj_bool_t sdp_x_ms_msg_index = -1; 
    456     int i; 
    457     int mcnt; 
    458     const pj_media_stream_info *mi[PJSDP_MAX_MEDIA]; 
    459     int has_active; 
    460     pj_media_sock_info sock_info; 
    461  
    462     /* Find "m=x-ms-message" line in the SDP. */ 
    463     for (i=0; i<(int)sdp->media_count; ++i) { 
    464         if (pj_stricmp2(&sdp->media[i]->desc.media, "x-ms-message")==0) 
    465             sdp_x_ms_msg_index = i; 
    466     } 
    467  
    468     /* 
    469      * Create media session. 
    470      */ 
    471     pj_memset(&sock_info, 0, sizeof(sock_info)); 
    472     sock_info.rtp_sock = global.rtp_sock; 
    473     sock_info.rtcp_sock = global.rtcp_sock; 
    474     pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); 
    475  
    476     dlg_data->msession = pj_media_session_create_from_sdp (global.mmgr, sdp, &sock_info); 
    477  
    478     /* A session will always be created, unless there is memory 
    479      * alloc problem. 
    480      */ 
    481     pj_assert(dlg_data->msession); 
    482  
    483     /* See if we can take the offer by checking that we have at least 
    484      * one media stream active. 
    485      */ 
    486     mcnt = pj_media_session_enum_streams(dlg_data->msession, PJSDP_MAX_MEDIA, mi); 
    487     for (i=0, has_active=0; i<mcnt; ++i) { 
    488         if (mi[i]->fmt_cnt>0 && mi[i]->dir!=PJ_MEDIA_DIR_NONE) { 
    489             has_active = 1; 
    490             break; 
    491         } 
    492     } 
    493  
    494     if (!has_active && sdp_x_ms_msg_index==-1) { 
    495         pjsip_tx_data *tdata; 
    496  
    497         /* Unable to accept remote's SDP.  
    498          * Answer with 488 (Not Acceptable Here) 
    499          */ 
    500         /* Create 488 response. */ 
    501         tdata = pjsip_dlg_answer(dlg, PJSIP_SC_NOT_ACCEPTABLE_HERE); 
    502  
    503         /* Send response. */ 
    504         if (tdata) 
    505             pjsip_dlg_send_msg(dlg, tdata); 
    506         return; 
    507     } 
    508  
    509     dlg_data->x_ms_msg_session = sdp_x_ms_msg_index; 
    510  
    511     /* Create msg body to be used later in 2xx/response */ 
    512     create_msg_body(dlg, 0); 
    513  
    514 } 
    515  
    516 /* This callback is called after an INVITE is received. */ 
    517 static void dlg_on_incoming(pjsip_dlg *dlg, pjsip_transaction *tsx, 
    518                             pjsip_rx_data *rdata) 
    519 { 
    520     struct dialog_data *dlg_data; 
    521     pjsip_msg *msg; 
    522     pjsip_tx_data *tdata; 
    523     char buf[128]; 
    524     int len; 
    525  
    526     PJ_UNUSED_ARG(tsx) 
    527  
    528     pj_assert(rdata->msg->type == PJSIP_REQUEST_MSG && 
    529               rdata->msg->line.req.method.id == PJSIP_INVITE_METHOD && 
    530               tsx->method.id == PJSIP_INVITE_METHOD); 
    531  
    532     /* 
    533      * Notify user! 
    534      */ 
    535     PJ_LOG(3, (THIS_FILE, "")); 
    536     PJ_LOG(3, (THIS_FILE, "INCOMING CALL ON DIALOG %s!!", dlg->obj_name)); 
    537     PJ_LOG(3, (THIS_FILE, "")); 
    538     len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,  
    539                            (pjsip_name_addr*)dlg->remote.info->uri,  
    540                            buf, sizeof(buf)-1); 
    541     if (len > 0) { 
    542         buf[len] = '\0'; 
    543         PJ_LOG(3,(THIS_FILE, "From:\t%s", buf)); 
    544     } 
    545     len = pjsip_uri_print( PJSIP_URI_IN_FROMTO_HDR,  
    546                            (pjsip_name_addr*)dlg->local.info->uri,  
    547                            buf, sizeof(buf)-1); 
    548     if (len > 0) { 
    549         buf[len] = '\0'; 
    550         PJ_LOG(3,(THIS_FILE, "To:\t%s", buf)); 
    551     } 
    552     PJ_LOG(3, (THIS_FILE, "Press 'a' to answer, or 'h' to hangup!!", dlg->obj_name)); 
    553     PJ_LOG(3, (THIS_FILE, "")); 
    554  
    555     /* 
    556      * Process incoming dialog. 
    557      */ 
    558  
    559     dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); 
    560     dlg->user_data = dlg_data; 
    561  
    562     /* Update contact. */ 
    563     pjsip_dlg_set_contact(dlg, &global.contact); 
    564  
    565     /* Initialize credentials. */ 
    566     pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); 
    567  
    568     /* Create media session if the request has "application/sdp" body. */ 
    569     msg = rdata->msg; 
    570     if (msg->body &&  
    571         pj_stricmp2(&msg->body->content_type.type, "application")==0 && 
    572         pj_stricmp2(&msg->body->content_type.subtype, "sdp")==0) 
    573     { 
    574         pjsdp_session_desc *sdp; 
    575  
    576         /* Parse SDP body, and instantiate media session based on remote's SDP. 
    577          * Then create our SDP body from the session. 
    578          */ 
    579         sdp = pjsdp_parse (msg->body->data, msg->body->len, rdata->pool); 
    580         if (!sdp) 
    581             goto send_answer; 
    582  
    583         create_session_from_sdp(dlg, sdp); 
    584  
    585     } else if (msg->body) { 
    586         /* The request has a message body other than "application/sdp" */ 
    587         pjsip_accept_hdr *accept; 
    588  
    589         /* Create response. */ 
    590         tdata = pjsip_dlg_answer(dlg, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE); 
    591  
    592         /* Add "Accept" header. */ 
    593         accept = pjsip_accept_hdr_create(tdata->pool); 
    594         accept->values[0] = pj_str("application/sdp"); 
    595         accept->count = 1; 
    596         pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)accept); 
    597  
    598         /* Send response. */ 
    599         pjsip_dlg_send_msg(dlg, tdata); 
    600         return; 
    601  
    602     } else { 
    603         /* The request has no message body. We can take this request, but 
    604          * no media session will be activated. 
    605          */ 
    606         /* Nothing to do here. */ 
    607     } 
    608  
    609 send_answer: 
    610     /* Immediately answer with 100 (or 180? */ 
    611     tdata = pjsip_dlg_answer( dlg, PJSIP_SC_RINGING ); 
    612     pjsip_dlg_send_msg(dlg, tdata); 
    613  
    614     /* Set current dialog to this dialog if we don't currently have 
    615      * current dialog. 
    616      */ 
    617     if (global.cur_dlg == NULL) { 
    618         global.cur_dlg = dlg; 
    619     } 
    620  
    621     /* Auto-answer if option is specified. */ 
    622     if (global.auto_answer >= 0) { 
    623         pj_time_val delay = { 0, 0}; 
    624         struct dialog_data *dlg_data = dlg->user_data; 
    625  
    626         PJ_LOG(4, (THIS_FILE, "Scheduling auto-answer in %d seconds",  
    627                               global.auto_answer)); 
    628  
    629         delay.sec = global.auto_answer; 
    630         dlg_data->auto_timer.user_data = dlg; 
    631         dlg_data->auto_timer.id = AUTO_ANSWER; 
    632         dlg_data->auto_timer.cb = &dlg_auto_timer_callback; 
    633         dlg_data->has_auto_timer = 1; 
    634         pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); 
    635     } 
    636 } 
    637  
    638 /* This callback is called when dialog has sent/received a provisional response 
    639  * to INVITE. 
    640  */ 
    641 static void dlg_on_provisional(pjsip_dlg *dlg, pjsip_transaction *tsx, 
    642                                pjsip_event *event) 
    643 { 
    644     const char *action; 
    645  
    646     pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && 
    647                event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && 
    648                event->src.tdata->msg->line.status.code/100 == 1 && 
    649                tsx->method.id == PJSIP_INVITE_METHOD)  
    650                || 
    651                (event->src_type == PJSIP_EVENT_RX_MSG && 
    652                event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && 
    653                event->src.rdata->msg->line.status.code/100 == 1 && 
    654                tsx->method.id == PJSIP_INVITE_METHOD)); 
    655  
    656     if (event->src_type == PJSIP_EVENT_TX_MSG) 
    657         action = "Sending"; 
    658     else 
    659         action = "Received"; 
    660  
    661     PJ_LOG(3, (THIS_FILE, "Dialog %s: %s %d (%s)",  
    662                           dlg->obj_name, action, tsx->status_code, 
    663                           pjsip_get_status_text(tsx->status_code)->ptr)); 
    664 } 
    665  
    666 /* This callback is called when 200 response to INVITE is sent/received. */ 
    667 static void dlg_on_connecting(pjsip_dlg *dlg, pjsip_event *event) 
    668 { 
    669     struct dialog_data *dlg_data = dlg->user_data; 
    670     const char *action; 
    671  
    672     pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && 
    673                event->src.tdata->msg->type == PJSIP_RESPONSE_MSG && 
    674                event->src.tdata->msg->line.status.code/100 == 2) 
    675                || 
    676                (event->src_type == PJSIP_EVENT_RX_MSG && 
    677                event->src.rdata->msg->type == PJSIP_RESPONSE_MSG && 
    678                event->src.rdata->msg->line.status.code/100 == 2)); 
    679  
    680     if (event->src_type == PJSIP_EVENT_RX_MSG) 
    681         action = "Received"; 
    682     else 
    683         action = "Sending"; 
    684  
    685     PJ_LOG(3, (THIS_FILE, "Dialog %s: %s 200 (OK)", dlg->obj_name, action)); 
    686  
    687     if (event->src_type == PJSIP_EVENT_RX_MSG) { 
    688         /* On receipt of 2xx response, negotiate our media capability 
    689          * and start media. 
    690          */ 
    691         pjsip_msg *msg = event->src.rdata->msg; 
    692         pjsip_msg_body *body; 
    693         pjsdp_session_desc *sdp; 
    694  
    695         /* Get SDP from message. */ 
    696  
    697         /* Ignore if no SDP body is present. */ 
    698         body = msg->body; 
    699         if (!body) { 
    700             PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no body!", 
    701                                   dlg->obj_name)); 
    702             return; 
    703         } 
    704  
    705         if (pj_stricmp2(&body->content_type.type, "application") != 0 && 
    706             pj_stricmp2(&body->content_type.subtype, "sdp") != 0)  
    707         { 
    708             PJ_LOG(3, (THIS_FILE, "Dialog %s: the 200/OK response has no SDP body!", 
    709                                    dlg->obj_name)); 
    710             return; 
    711         } 
    712  
    713         /* Got what seems to be a SDP content. Parse it. */ 
    714         sdp = pjsdp_parse (body->data, body->len, event->src.rdata->pool); 
    715         if (!sdp) { 
    716             PJ_LOG(3, (THIS_FILE, "Dialog %s: SDP syntax error!", 
    717                                   dlg->obj_name)); 
    718             return; 
    719         } 
    720  
    721         /* Negotiate media session with remote's media capability. */ 
    722         if (pj_media_session_update (dlg_data->msession, sdp) != 0) { 
    723             PJ_LOG(3, (THIS_FILE, "Dialog %s: media session update error!", 
    724                                   dlg->obj_name)); 
    725             return; 
    726         } 
    727  
    728         /* Update the saved SDP body because media session has changed.  
    729          * Also set ack flag to '1', because we only want to send one format/ 
    730          * codec for each media streams. 
    731          */ 
    732         create_msg_body(dlg, 1); 
    733  
    734         /* Activate media. */ 
    735         pj_media_session_activate (dlg_data->msession); 
    736  
    737     } else { 
    738         pjsip_msg *msg = event->src.tdata->msg; 
    739  
    740         if (msg->body) { 
    741             /* On transmission of 2xx response, start media session. */ 
    742             pj_media_session_activate (dlg_data->msession); 
    743         } 
    744     } 
    745  
    746 } 
    747  
    748 /* This callback is called when ACK to initial INVITE is sent/received. */ 
    749 static void dlg_on_established(pjsip_dlg *dlg, pjsip_event *event) 
    750 { 
    751     const char *action; 
    752  
    753     pj_assert((event->src_type == PJSIP_EVENT_TX_MSG && 
    754                event->src.tdata->msg->type == PJSIP_REQUEST_MSG && 
    755                event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) 
    756                || 
    757                (event->src_type == PJSIP_EVENT_RX_MSG && 
    758                event->src.rdata->msg->type == PJSIP_REQUEST_MSG && 
    759                event->src.rdata->msg->line.req.method.id == PJSIP_ACK_METHOD)); 
    760  
    761     if (event->src_type == PJSIP_EVENT_RX_MSG) 
    762         action = "Received"; 
    763     else 
    764         action = "Sending"; 
    765  
    766     PJ_LOG(3, (THIS_FILE, "Dialog %s: %s ACK, dialog is ESTABLISHED",  
    767                           dlg->obj_name, action)); 
    768  
    769     /* Attach SDP body for outgoing ACK. */ 
    770     if (event->src_type == PJSIP_EVENT_TX_MSG && 
    771         event->src.tdata->msg->line.req.method.id == PJSIP_ACK_METHOD) 
    772     { 
    773         struct dialog_data *dlg_data = dlg->user_data; 
    774         event->src.tdata->msg->body = dlg_data->body; 
    775     } 
    776  
    777     /* Auto-hangup if option is specified. */ 
    778     if (global.auto_hangup >= 0) { 
    779         pj_time_val delay = { 0, 0}; 
    780         struct dialog_data *dlg_data = dlg->user_data; 
    781  
    782         PJ_LOG(4, (THIS_FILE, "Scheduling auto-hangup in %d seconds",  
    783                               global.auto_hangup)); 
    784  
    785         delay.sec = global.auto_hangup; 
    786         dlg_data->auto_timer.user_data = dlg; 
    787         dlg_data->auto_timer.id = AUTO_HANGUP; 
    788         dlg_data->auto_timer.cb = &dlg_auto_timer_callback; 
    789         dlg_data->has_auto_timer = 1; 
    790         pjsip_endpt_schedule_timer(dlg->ua->endpt, &dlg_data->auto_timer, &delay); 
    791     } 
    792 } 
    793  
    794  
    795 /* This callback is called when dialog is disconnected (because of final 
    796  * response, BYE, or timer). 
    797  */ 
    798 static void dlg_on_disconnected(pjsip_dlg *dlg, pjsip_event *event) 
    799 { 
    800     struct dialog_data *dlg_data = dlg->user_data; 
    801     int status_code; 
    802     const pj_str_t *reason; 
    803      
    804     PJ_UNUSED_ARG(event) 
    805  
    806     /* Cancel auto-answer/auto-hangup timer. */ 
    807     if (dlg_data->has_auto_timer) { 
    808         pjsip_endpt_cancel_timer(dlg->ua->endpt, &dlg_data->auto_timer); 
    809         dlg_data->has_auto_timer = 0; 
    810     } 
    811  
    812     if (dlg->invite_tsx) 
    813         status_code = dlg->invite_tsx->status_code; 
    814     else 
    815         status_code = 200; 
    816  
    817     if (event->obj.tsx->method.id == PJSIP_INVITE_METHOD) { 
    818         if (event->src_type == PJSIP_EVENT_RX_MSG) 
    819             reason = &event->src.rdata->msg->line.status.reason; 
    820         else if (event->src_type == PJSIP_EVENT_TX_MSG) 
    821             reason = &event->src.tdata->msg->line.status.reason; 
    822         else 
    823             reason = pjsip_get_status_text(event->obj.tsx->status_code); 
    824     } else { 
    825         reason = &event->obj.tsx->method.name; 
    826     } 
    827  
    828     PJ_LOG(3, (THIS_FILE, "Dialog %s: DISCONNECTED! Reason=%d (%.*s)",  
    829                           dlg->obj_name, status_code,  
    830                           reason->slen, reason->ptr)); 
    831  
    832     if (dlg_data->msession) { 
    833         pj_media_session_destroy (dlg_data->msession); 
    834         dlg_data->msession = NULL; 
    835     } 
    836 } 
    837  
    838 /* This callback is called when dialog is about to be destroyed. */ 
    839 static void dlg_on_terminated(pjsip_dlg *dlg) 
    840 { 
    841     PJ_LOG(3, (THIS_FILE, "Dialog %s: terminated!", dlg->obj_name)); 
    842  
    843     /* If current dialog is equal to this dialog, update it. */ 
    844     if (global.cur_dlg == dlg) { 
    845         global.cur_dlg = global.cur_dlg->next; 
    846         if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { 
    847             global.cur_dlg = NULL; 
    848         } 
    849     } 
    850 } 
    851  
    852 /* This callback is called for any requests when dialog is established. */ 
    853 static void dlg_on_mid_call_evt (pjsip_dlg *dlg, pjsip_event *event) 
    854 { 
    855     pjsip_transaction *tsx = event->obj.tsx; 
    856  
    857     if (event->src_type == PJSIP_EVENT_RX_MSG && 
    858         event->src.rdata->msg->type == PJSIP_REQUEST_MSG)  
    859     { 
    860         if (event->src.rdata->cseq->method.id == PJSIP_INVITE_METHOD) { 
    861             /* Re-invitation. */ 
    862             pjsip_tx_data *tdata; 
    863  
    864             PJ_LOG(3,(THIS_FILE, "Dialog %s: accepting re-invitation (dummy)", 
    865                                  dlg->obj_name)); 
    866             tdata = pjsip_dlg_answer(dlg, 200); 
    867             if (tdata) { 
    868                 struct dialog_data *dlg_data = dlg->user_data; 
    869                 tdata->msg->body = dlg_data->body; 
    870                 pjsip_dlg_send_msg(dlg, tdata); 
    871             } 
    872         } else { 
    873             /* Don't worry, endpoint will answer with 500 or whetever. */ 
    874         } 
    875  
    876     } else if (tsx->status_code/100 == 2) { 
    877         PJ_LOG(3,(THIS_FILE, "Dialog %s: outgoing %.*s success: %d (%s)", 
    878                   dlg->obj_name,  
    879                   tsx->method.name.slen, tsx->method.name.ptr, 
    880                   tsx->status_code,  
    881                   pjsip_get_status_text(tsx->status_code)->ptr)); 
    882  
    883  
    884     } else if (tsx->status_code >= 300) { 
    885         pj_bool_t report_failure = PJ_TRUE; 
    886  
    887         /* Check for authentication failures. */ 
    888         if (tsx->status_code==401 || tsx->status_code==407) { 
    889             pjsip_tx_data *tdata; 
    890             tdata = pjsip_auth_reinit_req( global.endpt, 
    891                                            dlg->pool, &dlg->auth_sess, 
    892                                            dlg->cred_count, dlg->cred_info, 
    893                                            tsx->last_tx, event->src.rdata ); 
    894             if (tdata) { 
    895                 int rc; 
    896                 rc = pjsip_dlg_send_msg( dlg, tdata); 
    897                 report_failure = (rc != 0); 
    898             } 
    899         } 
    900         if (report_failure) { 
    901             const pj_str_t *reason; 
    902             if (event->src_type == PJSIP_EVENT_RX_MSG) { 
    903                 reason = &event->src.rdata->msg->line.status.reason; 
    904             } else { 
    905                 reason = pjsip_get_status_text(tsx->status_code); 
    906             } 
    907             PJ_LOG(2,(THIS_FILE, "Dialog %s: outgoing request failed: %d (%.*s)", 
    908                       dlg->obj_name, tsx->status_code,  
    909                       reason->slen, reason->ptr)); 
    910         } 
    911     } 
    912 } 
    913  
    914 /* Initialize sockets and optionally get the public address via STUN. */ 
    915 static pj_status_t init_sockets() 
    916 { 
    917     enum {  
    918         RTP_START_PORT = 4000, 
    919         RTP_RANDOM_START = 2, 
    920         RTP_RETRY = 10  
    921     }; 
    922     enum { 
    923         SIP_SOCK, 
    924         RTP_SOCK, 
    925         RTCP_SOCK, 
    926     }; 
    927     int i; 
    928     int rtp_port; 
    929     pj_sock_t sock[3]; 
    930     pj_sockaddr_in mapped_addr[3]; 
    931  
    932     for (i=0; i<3; ++i) 
    933         sock[i] = PJ_INVALID_SOCKET; 
    934  
    935     /* Create and bind SIP UDP socket. */ 
    936     sock[SIP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); 
    937     if (sock[SIP_SOCK] == PJ_INVALID_SOCKET) { 
    938         PJ_LOG(2,(THIS_FILE, "Unable to create socket")); 
    939         goto on_error; 
    940     } 
    941     if (pj_sock_bind_in(sock[SIP_SOCK], 0, (pj_uint16_t)global.sip_port) != 0) { 
    942         PJ_LOG(2,(THIS_FILE, "Unable to bind to SIP port")); 
    943         goto on_error; 
    944     } 
    945  
    946     /* Initialize start of RTP port to try. */ 
    947     rtp_port = RTP_START_PORT + (pj_rand() % RTP_RANDOM_START) / 2; 
    948  
    949     /* Loop retry to bind RTP and RTCP sockets. */ 
    950     for (i=0; i<RTP_RETRY; ++i, rtp_port += 2) { 
    951  
    952         /* Create and bind RTP socket. */ 
    953         sock[RTP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); 
    954         if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) 
    955             goto on_error; 
    956         if (pj_sock_bind_in(sock[RTP_SOCK], 0, (pj_uint16_t)rtp_port) != 0) { 
    957             pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; 
    958             continue; 
    959         } 
    960  
    961         /* Create and bind RTCP socket. */ 
    962         sock[RTCP_SOCK] = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, 0); 
    963         if (sock[RTCP_SOCK] == PJ_INVALID_SOCKET) 
    964             goto on_error; 
    965         if (pj_sock_bind_in(sock[RTCP_SOCK], 0, (pj_uint16_t)(rtp_port+1)) != 0) { 
    966             pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; 
    967             pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; 
    968             continue; 
    969         } 
    970  
    971         /* 
    972          * If we're configured to use STUN, then find out the mapped address, 
    973          * and make sure that the mapped RTCP port is adjacent with the RTP. 
    974          */ 
    975         if (global.stun_port1 == 0) { 
    976             pj_str_t hostname; 
    977             pj_sockaddr_in addr; 
    978  
    979             /* Get local IP address. */ 
    980             char hostname_buf[PJ_MAX_HOSTNAME]; 
    981             if (gethostname(hostname_buf, sizeof(hostname_buf))) 
    982                 goto on_error; 
    983             hostname = pj_str(hostname_buf); 
    984  
    985             pj_memset( &addr, 0, sizeof(addr)); 
    986             addr.sin_family = PJ_AF_INET; 
    987             if (pj_sockaddr_set_str_addr( &addr, &hostname) != PJ_SUCCESS) 
    988                 goto on_error; 
    989  
    990             for (i=0; i<3; ++i) 
    991                 pj_memcpy(&mapped_addr[i], &addr, sizeof(addr)); 
    992  
    993             mapped_addr[SIP_SOCK].sin_port = pj_htons((pj_uint16_t)global.sip_port); 
    994             mapped_addr[RTP_SOCK].sin_port = pj_htons((pj_uint16_t)rtp_port); 
    995             mapped_addr[RTCP_SOCK].sin_port = pj_htons((pj_uint16_t)(rtp_port+1)); 
    996             break; 
    997         } else { 
    998             pj_status_t rc; 
    999             rc = pj_stun_get_mapped_addr( global.pf, 3, sock, 
    1000                                           &global.stun_srv1, global.stun_port1, 
    1001                                           &global.stun_srv2, global.stun_port2, 
    1002                                           mapped_addr); 
    1003             if (rc != 0) { 
    1004                 PJ_LOG(3,(THIS_FILE, "Error: %s", pj_stun_get_err_msg(rc))); 
    1005                 goto on_error; 
    1006             } 
    1007  
    1008             if (pj_ntohs(mapped_addr[2].sin_port) == pj_ntohs(mapped_addr[1].sin_port)+1) 
    1009                 break; 
    1010  
    1011             pj_sock_close(sock[RTP_SOCK]); sock[RTP_SOCK] = PJ_INVALID_SOCKET; 
    1012             pj_sock_close(sock[RTCP_SOCK]); sock[RTCP_SOCK] = PJ_INVALID_SOCKET; 
    1013         } 
    1014     } 
    1015  
    1016     if (sock[RTP_SOCK] == PJ_INVALID_SOCKET) { 
    1017         PJ_LOG(2,(THIS_FILE, "Unable to find appropriate RTP/RTCP ports combination")); 
    1018         goto on_error; 
    1019     } 
    1020  
    1021     global.sip_sock = sock[SIP_SOCK]; 
    1022     pj_memcpy(&global.sip_sock_name, &mapped_addr[SIP_SOCK], sizeof(pj_sockaddr_in)); 
    1023     global.rtp_sock = sock[RTP_SOCK]; 
    1024     pj_memcpy(&global.rtp_sock_name, &mapped_addr[RTP_SOCK], sizeof(pj_sockaddr_in)); 
    1025     global.rtcp_sock = sock[RTCP_SOCK]; 
    1026     pj_memcpy(&global.rtcp_sock_name, &mapped_addr[RTCP_SOCK], sizeof(pj_sockaddr_in)); 
    1027  
    1028     PJ_LOG(4,(THIS_FILE, "SIP UDP socket reachable at %s:%d", 
    1029               pj_inet_ntoa(global.sip_sock_name.sin_addr),  
    1030               pj_ntohs(global.sip_sock_name.sin_port))); 
    1031     PJ_LOG(4,(THIS_FILE, "RTP socket reachable at %s:%d", 
    1032               pj_inet_ntoa(global.rtp_sock_name.sin_addr),  
    1033               pj_ntohs(global.rtp_sock_name.sin_port))); 
    1034     PJ_LOG(4,(THIS_FILE, "RTCP UDP socket reachable at %s:%d", 
    1035               pj_inet_ntoa(global.rtcp_sock_name.sin_addr),  
    1036               pj_ntohs(global.rtcp_sock_name.sin_port))); 
     236 
     237    pjsip_endpt_register_module(pjsua.endpt, &console_msg_logger); 
     238 
     239 
     240    /* Sleep for a while, let any messages get printed to console: */ 
     241 
     242    pj_thread_sleep(500); 
     243 
     244 
     245    /* Start UI console main loop: */ 
     246 
     247    ui_console_main(); 
     248 
     249 
     250    /* Destroy pjsua: */ 
     251 
     252    pjsua_destroy(); 
     253 
     254    /* Exit... */ 
     255 
    1037256    return 0; 
    1038  
    1039 on_error: 
    1040     for (i=0; i<3; ++i) { 
    1041         if (sock[i] != PJ_INVALID_SOCKET) 
    1042             pj_sock_close(sock[i]); 
    1043     } 
    1044     return -1; 
    1045 } 
    1046  
    1047 static void log_function(int level, const char *buffer, int len) 
    1048 { 
    1049     /* Write to both stdout and file. */ 
    1050     if (level <= global.app_log_level) 
    1051         pj_log_to_stdout(level, buffer, len); 
    1052     if (global.log_file) { 
    1053         fwrite(buffer, len, 1, global.log_file); 
    1054         fflush(global.log_file); 
    1055     } 
    1056 } 
    1057  
    1058 /* Initialize stack. */ 
    1059 static pj_status_t init_stack() 
    1060 { 
    1061     pj_status_t status; 
    1062     pj_sockaddr_in bind_addr; 
    1063     pj_sockaddr_in bind_name; 
    1064     const char *local_addr; 
    1065     static char local_uri[128]; 
    1066  
    1067     /* Optionally set logging file. */ 
    1068     if (global.log_filename) { 
    1069         global.log_file = fopen(global.log_filename, "wt"); 
    1070     } 
    1071  
    1072     /* Initialize endpoint. This will also call initialization to all the 
    1073      * modules. 
    1074      */ 
    1075     global.endpt = pjsip_endpt_create(global.pf); 
    1076     if (global.endpt == NULL) { 
    1077         return -1; 
    1078     } 
    1079  
    1080     /* Set dialog callback. */ 
    1081     pjsip_ua_set_dialog_callback(global.user_agent, &dlg_callback); 
    1082  
    1083     /* Init listener's bound address and port. */ 
    1084     pj_sockaddr_init2(&bind_addr, "0.0.0.0", global.sip_port); 
    1085     pj_sockaddr_init(&bind_name, pj_gethostname(), global.sip_port); 
    1086  
    1087     /* Add UDP transport listener. */ 
    1088     status = pjsip_endpt_create_udp_listener( global.endpt, global.sip_sock, 
    1089                                               &global.sip_sock_name); 
    1090     if (status != 0) 
    1091         return -1; 
    1092  
    1093     local_addr = pj_inet_ntoa(global.sip_sock_name.sin_addr); 
    1094  
    1095 #if PJ_HAS_TCP 
    1096     /* Add TCP transport listener. */ 
    1097     status = pjsip_endpt_create_listener( global.endpt, PJSIP_TRANSPORT_TCP,  
    1098                                           &bind_addr, &bind_name); 
    1099     if (status != 0) 
    1100         return -1; 
    1101 #endif 
    1102  
    1103     /* Determine user_id to be put in Contact */ 
    1104     if (global.local_uri.slen) { 
    1105         pj_pool_t *pool = pj_pool_create(global.pf, "parser", 1024, 0, NULL); 
    1106         pjsip_uri *uri; 
    1107  
    1108         uri = pjsip_parse_uri(pool, global.local_uri.ptr, global.local_uri.slen, 0); 
    1109         if (uri) { 
    1110             if (pj_stricmp2(pjsip_uri_get_scheme(uri), "sip")==0) { 
    1111                 pjsip_sip_uri *url = (pjsip_sip_uri*)pjsip_uri_get_uri(uri); 
    1112                 if (url->user.slen) 
    1113                     strncpy(global.user_id, url->user.ptr, url->user.slen); 
    1114             } 
    1115         }  
    1116         pj_pool_release(pool); 
    1117     }  
    1118      
    1119     if (global.user_id[0]=='\0') { 
    1120         pj_native_strcpy(global.user_id, "user"); 
    1121     } 
    1122  
    1123     /* build contact */ 
    1124     global.real_contact.ptr = local_uri; 
    1125     global.real_contact.slen =  
    1126         sprintf(local_uri, "<sip:%s@%s:%d>", global.user_id, local_addr, global.sip_port); 
    1127  
    1128     if (global.contact.slen == 0) 
    1129         global.contact = global.real_contact; 
    1130  
    1131     /* initialize local_uri with contact if it's not specified in cmdline */ 
    1132     if (global.local_uri.slen == 0) 
    1133         global.local_uri = global.contact; 
    1134  
    1135     /* Init proxy. */ 
    1136     if (global.proxy.slen || global.outbound_proxy.slen) { 
    1137         int count = 0; 
    1138         pj_str_t proxy_url[2]; 
    1139  
    1140         if (global.outbound_proxy.slen) { 
    1141             proxy_url[count++] = global.outbound_proxy; 
    1142         } 
    1143         if (global.proxy.slen) { 
    1144             proxy_url[count++] = global.proxy; 
    1145         } 
    1146  
    1147         if (pjsip_endpt_set_proxies(global.endpt, count, proxy_url) != 0) { 
    1148             PJ_LOG(2,(THIS_FILE, "Error setting proxy address!")); 
    1149             return -1; 
    1150         } 
    1151     } 
    1152  
    1153     /* initialize SIP registration if registrar is configured */ 
    1154     if (global.registrar_uri.slen) { 
    1155         global.regc = pjsip_regc_create( global.endpt, NULL, &regc_cb); 
    1156         pjsip_regc_init( global.regc, &global.registrar_uri,  
    1157                          &global.local_uri,  
    1158                          &global.local_uri, 
    1159                          1, &global.contact,  
    1160                          global.reg_timeout); 
    1161         pjsip_regc_set_credentials( global.regc, global.cred_count, global.cred_info ); 
    1162     } 
    1163  
    1164     return PJ_SUCCESS; 
    1165 } 
    1166  
    1167 /* Worker thread function, only used when threading is enabled. */ 
    1168 static void *PJ_THREAD_FUNC worker_thread(void *unused) 
    1169 { 
    1170     PJ_UNUSED_ARG(unused) 
    1171  
    1172     while (!global.worker_quit_flag) { 
    1173         pj_time_val timeout = { 0, 10 }; 
    1174         pjsip_endpt_handle_events (global.endpt, &timeout); 
    1175     } 
    1176     return NULL; 
    1177 } 
    1178  
    1179  
    1180 /* Make call to the specified URI. */ 
    1181 static pjsip_dlg *make_call(pj_str_t *remote_uri) 
    1182 { 
    1183     pjsip_dlg *dlg; 
    1184     pj_str_t local = global.contact; 
    1185     pj_str_t remote = *remote_uri; 
    1186     struct dialog_data *dlg_data; 
    1187     pjsip_tx_data *tdata; 
    1188     pj_media_sock_info sock_info; 
    1189  
    1190     /* Create new dialog instance. */ 
    1191     dlg = pjsip_ua_create_dialog(global.user_agent, PJSIP_ROLE_UAC); 
    1192  
    1193     /* Attach our own user data. */ 
    1194     dlg_data = pj_pool_calloc(dlg->pool, 1, sizeof(struct dialog_data)); 
    1195     dlg->user_data = dlg_data; 
    1196  
    1197     /* Create media session. */ 
    1198     pj_memset(&sock_info, 0, sizeof(sock_info)); 
    1199     sock_info.rtp_sock = global.rtp_sock; 
    1200     sock_info.rtcp_sock = global.rtcp_sock; 
    1201     pj_memcpy(&sock_info.rtp_addr_name, &global.rtp_sock_name, sizeof(pj_sockaddr_in)); 
    1202  
    1203     dlg_data->msession = pj_media_session_create (global.mmgr, &sock_info); 
    1204     dlg_data->x_ms_msg_session = -1; 
    1205  
    1206     if (global.offer_x_ms_msg) { 
    1207         const pj_media_stream_info *minfo[32]; 
    1208         unsigned cnt; 
    1209  
    1210         cnt = pj_media_session_enum_streams(dlg_data->msession, 32, minfo); 
    1211         if (cnt > 0) 
    1212             dlg_data->x_ms_msg_session = cnt; 
    1213     }  
    1214  
    1215     /* Initialize dialog with local and remote URI. */ 
    1216     if (pjsip_dlg_init(dlg, &local, &remote, NULL) != PJ_SUCCESS) { 
    1217         pjsip_ua_destroy_dialog(dlg); 
    1218         return NULL; 
    1219     } 
    1220  
    1221     /* Initialize credentials. */ 
    1222     pjsip_dlg_set_credentials(dlg, global.cred_count, global.cred_info); 
    1223  
    1224     /* Send INVITE! */ 
    1225     tdata = pjsip_dlg_invite(dlg); 
    1226     tdata->msg->body = create_msg_body (dlg, 0); 
    1227  
    1228     if (pjsip_dlg_send_msg(dlg, tdata) != PJ_SUCCESS) { 
    1229         pjsip_ua_destroy_dialog(dlg); 
    1230         return NULL; 
    1231     } 
    1232  
    1233     return dlg; 
    1234 } 
    1235  
    1236 /* 
    1237  * Callback to receive incoming IM message. 
    1238  */ 
    1239 static int on_incoming_im_msg(pjsip_rx_data *rdata) 
    1240 { 
    1241     pjsip_msg *msg = rdata->msg; 
    1242     pjsip_msg_body *body = msg->body; 
    1243     int len; 
    1244     char to[128], from[128]; 
    1245  
    1246  
    1247     len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,  
    1248                            rdata->from->uri, from, sizeof(from)); 
    1249     if (len > 0) from[len] = '\0'; 
    1250     else pj_native_strcpy(from, "<URL too long..>"); 
    1251  
    1252     len = pjsip_uri_print( PJSIP_URI_IN_CONTACT_HDR,  
    1253                            rdata->to->uri, to, sizeof(to)); 
    1254     if (len > 0) to[len] = '\0'; 
    1255     else pj_native_strcpy(to, "<URL too long..>"); 
    1256  
    1257     PJ_LOG(3,(THIS_FILE, "Incoming instant message:")); 
    1258      
    1259     printf("----- BEGIN INSTANT MESSAGE ----->\n"); 
    1260     printf("From:\t%s\n", from); 
    1261     printf("To:\t%s\n", to); 
    1262     printf("Body:\n%.*s\n", (body ? body->len : 0), (body ? (char*)body->data : "")); 
    1263     printf("<------ END INSTANT MESSAGE ------\n"); 
    1264  
    1265     fflush(stdout); 
    1266  
    1267     /* Must answer with final response. */ 
    1268     return 200; 
    1269 } 
    1270  
    1271 /* 
    1272  * Input URL. 
    1273  */ 
    1274 static pj_str_t *ui_input_url(pj_str_t *out, char *buf, int len, int *selection) 
    1275 { 
    1276     int i; 
    1277  
    1278     *selection = -1; 
    1279  
    1280     printf("\nBuddy list:\n"); 
    1281     printf("---------------------------------------\n"); 
    1282     for (i=0; i<global.buddy_cnt; ++i) { 
    1283         printf(" %d\t%s  <%s>\n", i+1, global.buddy[i].ptr, 
    1284                 (global.buddy_status[i]?"Online":"Offline")); 
    1285     } 
    1286     printf("-------------------------------------\n"); 
    1287  
    1288     printf("Choices\n" 
    1289            "\t0        For current dialog.\n" 
    1290            "\t[1-%02d]   Select from buddy list\n" 
    1291            "\tURL      An URL\n" 
    1292            , global.buddy_cnt); 
    1293     printf("Input: "); 
    1294  
    1295     fflush(stdout); 
    1296     fgets(buf, len, stdin); 
    1297     buf[strlen(buf)-1] = '\0'; /* remove trailing newline. */ 
    1298  
    1299     while (isspace(*buf)) ++buf; 
    1300  
    1301     if (!*buf || *buf=='\n' || *buf=='\r') 
    1302         return NULL; 
    1303  
    1304     i = atoi(buf); 
    1305  
    1306     if (i == 0) { 
    1307         if (isdigit(*buf)) { 
    1308             *selection = 0; 
    1309             *out = pj_str("0"); 
    1310             return out; 
    1311         } else { 
    1312             if (verify_sip_url(buf) != 0) { 
    1313                 puts("Invalid URL specified!"); 
    1314                 return NULL; 
    1315             } 
    1316             *out = pj_str(buf); 
    1317             return out; 
    1318         } 
    1319     } else if (i > global.buddy_cnt || i < 0) { 
    1320         printf("Error: invalid selection!\n"); 
    1321         return NULL; 
    1322     } else { 
    1323         *out = global.buddy[i-1]; 
    1324         *selection = i; 
    1325         return out; 
    1326     } 
    1327 } 
    1328  
    1329  
    1330 static void generic_request_callback( void *token, pjsip_event *event ) 
    1331 { 
    1332     pjsip_transaction *tsx = event->obj.tsx; 
    1333      
    1334     PJ_UNUSED_ARG(token) 
    1335  
    1336     if (tsx->status_code/100 == 2) { 
    1337         PJ_LOG(3,(THIS_FILE, "Outgoing %.*s %d (%s)", 
    1338                   event->obj.tsx->method.name.slen, 
    1339                   event->obj.tsx->method.name.ptr, 
    1340                   tsx->status_code, 
    1341                   pjsip_get_status_text(tsx->status_code)->ptr)); 
    1342     } else if (tsx->status_code==401 || tsx->status_code==407)  { 
    1343         pjsip_tx_data *tdata; 
    1344         tdata = pjsip_auth_reinit_req( global.endpt, 
    1345                                        global.pool, NULL, global.cred_count, global.cred_info, 
    1346                                        tsx->last_tx, event->src.rdata); 
    1347         if (tdata) { 
    1348             int rc; 
    1349             pjsip_cseq_hdr *cseq; 
    1350             cseq = (pjsip_cseq_hdr*)pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); 
    1351             cseq->cseq++; 
    1352             rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,  
    1353                                             &generic_request_callback); 
    1354             if (rc == 0) 
    1355                 return; 
    1356         } 
    1357         PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%s)", 
    1358                   event->obj.tsx->method.name.slen, 
    1359                   event->obj.tsx->method.name.ptr, 
    1360                   event->obj.tsx->status_code, 
    1361                   pjsip_get_status_text(event->obj.tsx->status_code)->ptr)); 
    1362     } else { 
    1363         const pj_str_t *reason; 
    1364         if (event->src_type == PJSIP_EVENT_RX_MSG) 
    1365             reason = &event->src.rdata->msg->line.status.reason; 
    1366         else 
    1367             reason = pjsip_get_status_text(tsx->status_code); 
    1368         PJ_LOG(2,(THIS_FILE, "Outgoing %.*s failed, status=%d (%.*s)", 
    1369                   event->obj.tsx->method.name.slen, 
    1370                   event->obj.tsx->method.name.ptr, 
    1371                   event->obj.tsx->status_code, 
    1372                   reason->slen, reason->ptr)); 
    1373     } 
    1374 } 
    1375  
    1376  
    1377 static void ui_send_im_message() 
    1378 { 
    1379     char line[100]; 
    1380     char text_buf[100]; 
    1381     pj_str_t str; 
    1382     pj_str_t text_msg; 
    1383     int selection, rc; 
    1384     pjsip_tx_data *tdata; 
    1385    
    1386     if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) 
    1387         return; 
    1388  
    1389          
    1390     printf("Enter text to send (empty to cancel): "); fflush(stdout); 
    1391     fgets(text_buf, sizeof(text_buf), stdin); 
    1392     text_buf[strlen(text_buf)-1] = '\0'; 
    1393     if (!*text_buf) 
    1394         return; 
    1395  
    1396     text_msg = pj_str(text_buf); 
    1397      
    1398     if (selection==0) { 
    1399         pjsip_method message_method; 
    1400         pj_str_t str_MESSAGE = { "MESSAGE", 7 }; 
    1401  
    1402         /* Send IM to current dialog. */ 
    1403         if (global.cur_dlg == NULL || global.cur_dlg->state != PJSIP_DIALOG_STATE_ESTABLISHED) { 
    1404             printf("No current dialog or dialog state is not ESTABLISHED!\n"); 
    1405             return; 
    1406         } 
    1407  
    1408         pjsip_method_init( &message_method, global.cur_dlg->pool, &str_MESSAGE); 
    1409         tdata = pjsip_dlg_create_request( global.cur_dlg, &message_method, -1 ); 
    1410  
    1411         if (tdata) { 
    1412             /* Create message body for the text. */ 
    1413             pjsip_msg_body *body = pj_pool_calloc(tdata->pool, 1, sizeof(*body)); 
    1414             body->content_type.type = pj_str("text"); 
    1415             body->content_type.subtype = pj_str("plain"); 
    1416             body->data = pj_pool_alloc(tdata->pool, text_msg.slen); 
    1417             pj_memcpy(body->data, text_msg.ptr, text_msg.slen); 
    1418             body->len = text_msg.slen; 
    1419             body->print_body = &pjsip_print_text_body; 
    1420  
    1421             /* Assign body to message, and send the message! */ 
    1422             tdata->msg->body = body; 
    1423             pjsip_dlg_send_msg( global.cur_dlg, tdata ); 
    1424         } 
    1425  
    1426     } else { 
    1427         /* Send IM to buddy list. */ 
    1428         pjsip_method message; 
    1429         static pj_str_t MESSAGE = { "MESSAGE", 7 }; 
    1430         pjsip_method_init_np(&message, &MESSAGE); 
    1431         tdata = pjsip_endpt_create_request(global.endpt, &message,  
    1432                                            &str, 
    1433                                            &global.real_contact, 
    1434                                            &str, &global.real_contact, NULL, -1,  
    1435                                            &text_msg); 
    1436         if (!tdata) { 
    1437             puts("Error creating request"); 
    1438             return; 
    1439         } 
    1440         rc = pjsip_endpt_send_request(global.endpt, tdata, -1, NULL, &generic_request_callback); 
    1441         if (rc == 0) { 
    1442             printf("Sending IM message %d\n", global.im_counter); 
    1443             ++global.im_counter; 
    1444         } else { 
    1445             printf("Error: unable to send IM message!\n"); 
    1446         } 
    1447     } 
    1448 } 
    1449  
    1450 static void ui_send_options() 
    1451 { 
    1452     char line[100]; 
    1453     pj_str_t str; 
    1454     int selection, rc; 
    1455     pjsip_tx_data *tdata; 
    1456     pjsip_method options; 
    1457  
    1458     if (ui_input_url(&str, line, sizeof(line), &selection) == NULL) 
    1459         return; 
    1460  
    1461     pjsip_method_set( &options, PJSIP_OPTIONS_METHOD ); 
    1462  
    1463     if (selection == 0) { 
    1464         /* Send OPTIONS to current dialog. */ 
    1465         tdata = pjsip_dlg_create_request(global.cur_dlg, &options, -1); 
    1466         if (tdata) 
    1467             pjsip_dlg_send_msg( global.cur_dlg, tdata ); 
    1468     } else { 
    1469         /* Send OPTIONS to arbitrary party. */ 
    1470         tdata = pjsip_endpt_create_request( global.endpt, &options, 
    1471                                             &str, 
    1472                                             &global.local_uri, &str,  
    1473                                             &global.real_contact, 
    1474                                             NULL, -1, NULL); 
    1475         if (tdata) { 
    1476             rc = pjsip_endpt_send_request( global.endpt, tdata, -1, NULL,  
    1477                                            &generic_request_callback); 
    1478             if (rc != 0) 
    1479                 PJ_LOG(2,(THIS_FILE, "Error sending OPTIONS!")); 
    1480         } 
    1481     } 
    1482 } 
    1483  
    1484 static void init_presence() 
    1485 { 
    1486     const pjsip_presence_cb pres_cb = { 
    1487         NULL, 
    1488         &pres_on_received_request, 
    1489         &pres_on_received_refresh, 
    1490         &pres_on_received_update, 
    1491         &pres_on_terminated 
    1492     }; 
    1493  
    1494     pjsip_presence_init(&pres_cb); 
    1495 } 
    1496  
    1497 /* Subscribe presence information for all buddies. */ 
    1498 static void subscribe_buddies_presence() 
    1499 { 
    1500     int i; 
    1501     for (i=0; i<global.buddy_cnt; ++i) { 
    1502         pjsip_presentity *pres; 
    1503         if (global.buddy_pres[i]) 
    1504             continue; 
    1505         pres = pjsip_presence_create( global.endpt, &global.local_uri, 
    1506                                       &global.buddy[i], PRESENCE_TIMEOUT, (void*)i); 
    1507         if (pres) { 
    1508             pjsip_presence_set_credentials( pres, global.cred_count, global.cred_info ); 
    1509             pjsip_presence_subscribe( pres ); 
    1510         } 
    1511         global.buddy_pres[i] = pres; 
    1512     } 
    1513 } 
    1514  
    1515 /* Unsubscribe presence information for all buddies. */ 
    1516 static void unsubscribe_buddies_presence() 
    1517 { 
    1518     int i; 
    1519     for (i=0; i<global.buddy_cnt; ++i) { 
    1520         pjsip_presentity *pres = global.buddy_pres[i]; 
    1521         if (pres) { 
    1522             pjsip_presence_unsubscribe(pres); 
    1523             pjsip_presence_destroy(pres); 
    1524             global.buddy_pres[i] = NULL; 
    1525         } 
    1526     } 
    1527 } 
    1528  
    1529 /* Unsubscribe presence. */ 
    1530 static void unsubscribe_presence() 
    1531 { 
    1532     int i; 
    1533  
    1534     unsubscribe_buddies_presence(); 
    1535     for (i=0; i<global.pres_cnt; ++i) { 
    1536         pjsip_presentity *pres = global.pres[i]; 
    1537         pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_TERMINATED, 0); 
    1538         pjsip_presence_destroy( pres ); 
    1539     } 
    1540 } 
    1541  
    1542 /* Advertise online status to subscribers. */ 
    1543 static void update_im_status() 
    1544 { 
    1545     int i; 
    1546     for (i=0; i<global.pres_cnt; ++i) { 
    1547         pjsip_presentity *pres = global.pres[i]; 
    1548         pjsip_presence_notify( pres, PJSIP_EVENT_SUB_STATE_ACTIVE,  
    1549                                !global.hide_status); 
    1550     } 
    1551 } 
    1552  
    1553 /* 
    1554  * Main program. 
    1555  */ 
    1556 int main(int argc, char *argv[]) 
    1557 { 
    1558     /* set to WORKER_COUNT+1 to avoid zero size warning  
    1559      * when threading is disabled. */ 
    1560     pj_thread_t *thread[WORKER_COUNT+1]; 
    1561     pj_caching_pool cp; 
    1562     int i; 
    1563  
    1564     global.sip_port = 5060; 
    1565     global.auto_answer = -1; 
    1566     global.auto_hangup = -1; 
    1567     global.app_log_level = 3; 
    1568  
    1569     pj_log_set_level(4); 
    1570     pj_log_set_log_func(&log_function); 
    1571  
    1572     /* Init PJLIB */ 
    1573     if (pj_init() != PJ_SUCCESS) 
    1574         return 1; 
    1575  
    1576     /* Init caching pool. */ 
    1577     pj_caching_pool_init(&cp, &pj_pool_factory_default_policy, 0); 
    1578     global.pf = &cp.factory; 
    1579  
    1580     /* Create memory pool for application. */ 
    1581     global.pool = pj_pool_create(global.pf, "main", 1024, 0, NULL); 
    1582  
    1583     /* Parse command line arguments. */ 
    1584     if (parse_args(global.pool, argc, argv) != PJ_SUCCESS) { 
    1585         pj_caching_pool_destroy(&cp); 
    1586         return 1; 
    1587     } 
    1588  
    1589     /* Init sockets */ 
    1590     if (init_sockets() != 0) { 
    1591         pj_caching_pool_destroy(&cp); 
    1592         return 1; 
    1593     } 
    1594  
    1595     /* Initialize stack. */ 
    1596     if (init_stack() != PJ_SUCCESS) { 
    1597         pj_caching_pool_destroy(&cp); 
    1598         return 1; 
    1599     } 
    1600  
    1601     /* Set callback to receive incoming IM */ 
    1602     pjsip_messaging_set_incoming_callback( &on_incoming_im_msg ); 
    1603  
    1604     /* Set default worker count (can be zero) */ 
    1605     global.worker_cnt = WORKER_COUNT; 
    1606  
    1607     /* Create user worker thread(s), only when threading is enabled. */ 
    1608     for (i=0; i<global.worker_cnt; ++i) { 
    1609         thread[i] = pj_thread_create( global.pool, "sip%p",  
    1610                                       &worker_thread,  
    1611                                       NULL, 0, NULL, 0); 
    1612         if (thread == NULL) { 
    1613             global.worker_quit_flag = 1; 
    1614             for (--i; i>=0; --i) { 
    1615                 pj_thread_join(thread[i]); 
    1616                 pj_thread_destroy(thread[i]); 
    1617             } 
    1618             pj_caching_pool_destroy(&cp); 
    1619             return 1; 
    1620         } 
    1621     } 
    1622  
    1623     printf("Worker thread count: %d\n", global.worker_cnt); 
    1624  
    1625     /* Perform registration, if required. */ 
    1626     if (global.regc) { 
    1627         update_registration(global.regc, 1); 
    1628     } 
    1629  
    1630     /* Initialize media manager. */ 
    1631     global.mmgr = pj_med_mgr_create(global.pf); 
    1632  
    1633     /* Init presence. */ 
    1634     init_presence(); 
    1635  
    1636     /* Subscribe presence information of all buddies. */ 
    1637     if (!global.no_presence) 
    1638         subscribe_buddies_presence(); 
    1639  
    1640     /* Initializatio completes, loop waiting for commands. */ 
    1641     for (;!global.worker_quit_flag;) { 
    1642         pj_str_t str; 
    1643         char line[128]; 
    1644  
    1645 #if WORKER_COUNT==0 
    1646         /* If worker thread does not exist, main thread must poll for evetns.  
    1647          * But this won't work very well since main thread is blocked by  
    1648          * fgets(). So keep pressing the ENTER key to get the events! 
    1649          */ 
    1650         pj_time_val timeout = { 0, 100 }; 
    1651         pjsip_endpt_handle_events(global.endpt, &timeout); 
    1652         puts("Keep pressing ENTER key to get the events!"); 
    1653 #endif 
    1654  
    1655         printf("\nCurrent dialog: "); 
    1656         print_dialog(global.cur_dlg); 
    1657         puts(""); 
    1658  
    1659         keystroke_help(); 
    1660  
    1661         fgets(line, sizeof(line), stdin); 
    1662  
    1663         switch (*line) { 
    1664         case 'm': 
    1665             puts("Make outgoing call"); 
    1666             if (ui_input_url(&str, line, sizeof(line), &i) != NULL) { 
    1667                 pjsip_dlg *dlg = make_call(&str); 
    1668                 if (global.cur_dlg == NULL) { 
    1669                     global.cur_dlg = dlg; 
    1670                 } 
    1671             } 
    1672             break; 
    1673         case 'i': 
    1674             puts("Send Instant Messaging"); 
    1675             ui_send_im_message(); 
    1676             break; 
    1677         case 'o': 
    1678             puts("Send OPTIONS"); 
    1679             ui_send_options(); 
    1680             break; 
    1681         case 'a': 
    1682             if (global.cur_dlg) { 
    1683                 unsigned code; 
    1684                 pjsip_tx_data *tdata; 
    1685                 struct dialog_data *dlg_data = global.cur_dlg->user_data; 
    1686  
    1687                 printf("Answer with status code (1xx-6xx): "); 
    1688                 fflush(stdout); 
    1689                 fgets(line, sizeof(line), stdin); 
    1690                 str = pj_str(line); 
    1691                 str.slen -= 1; 
    1692  
    1693                 code = pj_strtoul(&str); 
    1694                 tdata = pjsip_dlg_answer(global.cur_dlg, code); 
    1695                 if (tdata) { 
    1696                     if (code/100 == 2) { 
    1697                         tdata->msg->body = dlg_data->body; 
    1698                     } 
    1699                     pjsip_dlg_send_msg(global.cur_dlg, tdata); 
    1700  
    1701                 } 
    1702             } else { 
    1703                 puts("No current dialog"); 
    1704             } 
    1705             break; 
    1706         case 'h': 
    1707             if (global.cur_dlg) { 
    1708                 pjsip_tx_data *tdata; 
    1709                 tdata = pjsip_dlg_disconnect(global.cur_dlg, PJSIP_SC_DECLINE); 
    1710                 if (tdata) { 
    1711                     pjsip_dlg_send_msg(global.cur_dlg, tdata); 
    1712                 } 
    1713             } else { 
    1714                 puts("No current dialog"); 
    1715             } 
    1716             break; 
    1717         case ']': 
    1718             if (global.cur_dlg) { 
    1719                 global.cur_dlg = global.cur_dlg->next; 
    1720                 if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { 
    1721                     global.cur_dlg = global.cur_dlg->next; 
    1722                 } 
    1723             } else { 
    1724                 puts("No current dialog"); 
    1725             } 
    1726             break; 
    1727         case '[': 
    1728             if (global.cur_dlg) { 
    1729                 global.cur_dlg = global.cur_dlg->prev; 
    1730                 if (global.cur_dlg == (void*)&global.user_agent->dlg_list) { 
    1731                     global.cur_dlg = global.cur_dlg->prev; 
    1732                 } 
    1733             } else { 
    1734                 puts("No current dialog"); 
    1735             } 
    1736             break; 
    1737         case 'd': 
    1738             pjsip_endpt_dump(global.endpt, *(line+1)=='1'); 
    1739             pjsip_ua_dump(global.user_agent); 
    1740             break; 
    1741         case 's': 
    1742             if (*(line+1) == 'u') 
    1743                 subscribe_buddies_presence(); 
    1744             break; 
    1745         case 'u': 
    1746             if (*(line+1) == 's') 
    1747                 unsubscribe_presence(); 
    1748             break; 
    1749         case 't': 
    1750             global.hide_status = !global.hide_status; 
    1751             update_im_status(); 
    1752             break; 
    1753         case 'q': 
    1754             goto on_exit; 
    1755         case 'l': 
    1756             print_all_dialogs(); 
    1757             break; 
    1758         } 
    1759     } 
    1760  
    1761 on_exit: 
    1762     /* Unregister, if required. */ 
    1763     if (global.regc) { 
    1764         update_registration(global.regc, 0); 
    1765     } 
    1766  
    1767     /* Unsubscribe presence. */ 
    1768     unsubscribe_presence(); 
    1769  
    1770     /* Allow one second to get all events. */ 
    1771     if (1) { 
    1772         pj_time_val end_time; 
    1773  
    1774         pj_gettimeofday(&end_time); 
    1775         end_time.sec++; 
    1776  
    1777         PJ_LOG(3,(THIS_FILE, "Shutting down..")); 
    1778         for (;;) { 
    1779             pj_time_val timeout = { 0, 20 }, now; 
    1780             pjsip_endpt_handle_events (global.endpt, &timeout); 
    1781             pj_gettimeofday(&now); 
    1782             PJ_TIME_VAL_SUB(now, end_time); 
    1783             if (now.sec >= 1) 
    1784                 break; 
    1785         } 
    1786     } 
    1787  
    1788     global.worker_quit_flag = 1; 
    1789  
    1790     pj_med_mgr_destroy(global.mmgr); 
    1791  
    1792     /* Wait all threads to quit. */ 
    1793     for (i=0; i<global.worker_cnt; ++i) { 
    1794         pj_thread_join(thread[i]); 
    1795         pj_thread_destroy(thread[i]); 
    1796     } 
    1797  
    1798     /* Destroy endpoint. */ 
    1799     pjsip_endpt_destroy(global.endpt); 
    1800  
    1801     /* Destroy caching pool. */ 
    1802     pj_caching_pool_destroy(&cp); 
    1803  
    1804     /* Close log file, if any. */ 
    1805     if (global.log_file) 
    1806         fclose(global.log_file); 
    1807  
    1808     return 0; 
    1809 } 
    1810  
    1811 /* 
    1812  * Register static modules to the endpoint. 
    1813  */ 
    1814 pj_status_t register_static_modules( pj_size_t *count, 
    1815                                      pjsip_module **modules ) 
    1816 { 
    1817     /* Reset count. */ 
    1818     *count = 0; 
    1819  
    1820     /* Register user agent module. */ 
    1821     modules[(*count)++] = pjsip_ua_get_module(); 
    1822     global.user_agent = modules[0]->mod_data; 
    1823     modules[(*count)++] = pjsip_messaging_get_module(); 
    1824     modules[(*count)++] = pjsip_event_sub_get_module(); 
    1825  
    1826     return PJ_SUCCESS; 
    1827 } 
     257} 
     258 
Note: See TracChangeset for help on using the changeset viewer.