- Timestamp:
- Feb 7, 2006 12:34:11 PM (19 years ago)
- Location:
- pjproject/trunk/pjsip
- Files:
-
- 4 added
- 14 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjsip/build/pjsip_ua.dsp
r127 r139 42 42 # PROP Target_Dir "" 43 43 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c 44 # ADD CPP /nologo /MD /W4 /Zi /O2 /I "../ src" /I "../../pjlib/src" /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /FR /FD /c44 # 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 45 45 # SUBTRACT CPP /YX 46 46 # ADD BASE RSC /l 0x409 /d "NDEBUG" … … 66 66 # PROP Target_Dir "" 67 67 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c 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 /c68 # 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 69 69 # SUBTRACT CPP /YX 70 70 # ADD BASE RSC /l 0x409 /d "_DEBUG" … … 88 88 # Begin Source File 89 89 90 SOURCE="..\src\pjsip-ua\sip_inv.c" 91 # End Source File 92 # Begin Source File 93 90 94 SOURCE="..\src\pjsip-ua\sip_reg.c" 91 95 … … 105 109 # Begin Source File 106 110 111 SOURCE="..\include\pjsip-ua\sip_inv.h" 112 # End Source File 113 # Begin Source File 114 107 115 SOURCE="..\include\pjsip-ua\sip_regc.h" 108 116 # End Source File -
pjproject/trunk/pjsip/build/pjsua.dsp
r65 r139 43 43 # PROP Target_Dir "" 44 44 # 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 /c45 # 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 46 46 # SUBTRACT CPP /YX 47 47 # ADD BASE RSC /l 0x409 /d "NDEBUG" … … 69 69 # PROP Target_Dir "" 70 70 # 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 /c71 # 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 72 72 # SUBTRACT CPP /YX 73 73 # ADD BASE RSC /l 0x409 /d "_DEBUG" … … 96 96 97 97 SOURCE=..\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 98 107 # End Source File 99 108 # Begin Source File -
pjproject/trunk/pjsip/include/pjsip/sip_dialog.h
r133 r139 62 62 pj_mutex_t *mutex; /**< Dialog's mutex. */ 63 63 pjsip_user_agent *ua; /**< User agent instance. */ 64 pjsip_endpoint *endpt; /**< Endpoint instance. */ 64 65 65 66 /* The dialog set. */ -
pjproject/trunk/pjsip/include/pjsip/sip_event.h
r112 r139 58 58 PJSIP_EVENT_TSX_STATE, 59 59 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 69 60 /** Indicates that the event was triggered by user action. */ 70 61 PJSIP_EVENT_USER, 71 72 /** On before transmitting message. */73 PJSIP_EVENT_PRE_TX_MSG,74 62 75 63 } pjsip_event_id_e; … … 141 129 } tx_msg; 142 130 143 /** Pre-transmission event. */144 struct145 {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 151 131 /** Transmission error event. */ 152 132 struct … … 162 142 pjsip_transaction *tsx; /**< The transaction. */ 163 143 } rx_msg; 164 165 /** Receipt of 200/INVITE response. */166 struct167 {168 pjsip_rx_data *rdata; /**< The 200 response msg. */169 } rx_200_msg;170 171 /** Receipt of ACK message. */172 struct173 {174 pjsip_rx_data *rdata; /**< The ack message. */175 } rx_ack_msg;176 177 /** Notification that endpoint has discarded a message. */178 struct179 {180 pjsip_rx_data *rdata; /**< The discarded message. */181 pj_status_t reason;/**< The reason. */182 } discard_msg;183 144 184 145 /** User event. */ … … 246 207 247 208 /** 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 /**276 209 * Init user event. 277 210 */ … … 286 219 287 220 /** 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 /**300 221 * Get the event string from the event ID. 301 222 * @param e the event ID. -
pjproject/trunk/pjsip/include/pjsip/sip_msg.h
r127 r139 1822 1822 */ 1823 1823 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 */ 1837 typedef 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 */ 1850 PJ_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 */ 1867 PJ_DECL(pjsip_warning_hdr*) 1868 pjsip_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 1824 1876 /** 1825 1877 * @bug Once a header is put in the message, the header CAN NOT be put in … … 1954 2006 #define pjsip_user_agent_hdr_create pjsip_generic_string_hdr_create 1955 2007 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_create1961 2008 1962 2009 /** -
pjproject/trunk/pjsip/include/pjsip_ua.h
r66 r139 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 19 20 19 #ifndef __PJSIP_UA_H__ 21 20 #define __PJSIP_UA_H__ 22 21 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> 26 23 27 24 #endif /* __PJSIP_UA_H__ */ -
pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
r132 r139 38 38 #define THIS_FILE "sip_dialog.c" 39 39 40 long pjsip_dlg_lock_tls_id; 40 41 41 42 PJ_DEF(pj_bool_t) pjsip_method_creates_dialog(const pjsip_method *m) … … 73 74 pj_sprintf(dlg->obj_name, "dlg%p", dlg); 74 75 dlg->ua = ua; 76 dlg->endpt = endpt; 75 77 76 78 status = pj_mutex_create_recursive(pool, "dlg%p", &dlg->mutex); … … 93 95 if (dlg->mutex) 94 96 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); 96 98 } 97 99 … … 145 147 /* Randomize local CSeq. */ 146 148 dlg->local.first_cseq = pj_rand() % 0x7FFFFFFFL; 147 dlg->local.cseq = dlg->local. cseq;149 dlg->local.cseq = dlg->local.first_cseq; 148 150 149 151 /* Init local contact. */ … … 184 186 185 187 /* 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, 187 189 dlg->pool, 0); 188 190 if (status != PJ_SUCCESS) … … 343 345 344 346 /* 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, 346 348 dlg->pool, 0); 347 349 if (status != PJ_SUCCESS) … … 507 509 } 508 510 511 /* Log */ 512 PJ_LOG(5,(dlg->obj_name, "Dialog destroyed")); 513 509 514 /* Destroy this dialog. */ 510 515 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); 512 517 513 518 return PJ_SUCCESS; … … 630 635 dlg->mod_data[mod->id] = mod_data; 631 636 637 /* Increment count. */ 638 ++dlg->usage_cnt; 639 632 640 pj_mutex_unlock(dlg->mutex); 633 641 … … 664 672 * dialog. 665 673 */ 666 status = pjsip_endpt_create_request_from_hdr( pjsip_ua_get_endpt(dlg->ua),674 status = pjsip_endpt_create_request_from_hdr(dlg->endpt, 667 675 method, 668 676 dlg->target, … … 810 818 } 811 819 812 *p_tsx = tsx; 820 if (p_tsx) 821 *p_tsx = tsx; 813 822 814 823 } 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); 817 826 if (status != PJ_SUCCESS) 818 827 goto on_error; 819 828 820 *p_tsx = NULL; 829 if (p_tsx) 830 *p_tsx = NULL; 821 831 } 822 832 … … 833 843 pjsip_tx_data_dec_ref( tdata ); 834 844 835 *p_tsx = NULL; 845 if (p_tsx) 846 *p_tsx = NULL; 836 847 return status; 837 848 } … … 853 864 854 865 /* Create generic response. */ 855 status = pjsip_endpt_create_response( pjsip_ua_get_endpt(dlg->ua),866 status = pjsip_endpt_create_response(dlg->endpt, 856 867 rdata, st_code, st_text, &tdata); 857 868 if (status != PJ_SUCCESS) … … 915 926 if (st_class==2 || st_code==405) { 916 927 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, 918 929 PJSIP_H_ALLOW, NULL); 919 930 if (c_hdr) { … … 926 937 if (st_class==2) { 927 938 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, 929 940 PJSIP_H_SUPPORTED, NULL); 930 941 if (c_hdr) { … … 1057 1068 */ 1058 1069 pj_mutex_unlock(dlg->mutex); 1059 pjsip_endpt_respond_stateless( pjsip_ua_get_endpt(dlg->ua),1070 pjsip_endpt_respond_stateless(dlg->endpt, 1060 1071 rdata, 500, NULL, NULL, NULL); 1061 1072 return; … … 1095 1106 "Dialog will response with 500 (Internal Server Error)", 1096 1107 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, 1098 1109 rdata, 1099 1110 PJSIP_SC_INTERNAL_SERVER_ERROR, … … 1126 1137 pj_mutex_lock(dlg->mutex); 1127 1138 1139 /* Check that rdata already has dialog in mod_data. */ 1140 pj_assert(pjsip_rdata_get_dlg(rdata) == dlg); 1141 1128 1142 /* Update the remote tag, if none is specified yet. */ 1129 1143 if (dlg->remote.info->tag.slen == 0 && rdata->msg_info.to->tag.slen != 0) { … … 1134 1148 } 1135 1149 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 } 1138 1163 1139 1164 /* Pass to dialog usages. */ … … 1186 1211 dlg->sess_count == 0) 1187 1212 { 1213 /* Unregister this dialog from the transaction. */ 1214 tsx->mod_data[dlg->ua->id] = NULL; 1215 1188 1216 /* Time to destroy dialog. */ 1189 1217 unregister_and_destroy_dialog(dlg); -
pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
r127 r139 311 311 default: 312 312 return PJ_EINVAL; 313 } 314 315 if (hdr) { 316 pj_list_push_back(&endpt->cap_hdr, hdr); 313 317 } 314 318 } … … 756 760 757 761 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)); 758 767 } 759 768 -
pjproject/trunk/pjsip/src/pjsip/sip_msg.c
r127 r139 1703 1703 /////////////////////////////////////////////////////////////////////////////// 1704 1704 /* 1705 * Warning header. 1706 */ 1707 PJ_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 1725 PJ_DEF(pjsip_warning_hdr*) 1726 pjsip_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 /* 1705 1739 * Message body manipulations. 1706 1740 */ -
pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
r130 r139 32 32 33 33 #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 34 41 35 42 /***************************************************************************** … … 505 512 pj_mutex_lock(mod_tsx_layer.mutex); 506 513 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)); 517 527 518 528 /* 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); 519 532 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); 521 534 522 535 /* Unlock mutex. */ … … 539 552 540 553 /* 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); 541 557 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)); 543 564 544 565 /* Unlock mutex. */ … … 554 575 { 555 576 pjsip_transaction *tsx; 577 pj_uint32_t hval = 0; 556 578 557 579 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 ); 559 581 pj_mutex_unlock(mod_tsx_layer.mutex); 560 582 583 TSX_TRACE_((THIS_FILE, 584 "Finding tsx with hkey=0x%p and key=%.*s: found %p", 585 hval, key->slen, key->ptr, tsx)); 561 586 562 587 /* Race condition! … … 642 667 { 643 668 pj_str_t key; 669 pj_uint32_t hval = 0; 644 670 pjsip_transaction *tsx; 645 671 … … 650 676 pj_mutex_lock( mod_tsx_layer.mutex ); 651 677 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 653 685 654 686 if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { … … 683 715 { 684 716 pj_str_t key; 717 pj_uint32_t hval = 0; 685 718 pjsip_transaction *tsx; 686 719 … … 691 724 pj_mutex_lock( mod_tsx_layer.mutex ); 692 725 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 694 733 695 734 if (tsx == NULL || tsx->state == PJSIP_TSX_STATE_TERMINATED) { … … 919 958 } 920 959 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. */ 922 982 if (tsx->tsx_user && tsx->tsx_user->on_tsx_state) { 923 983 pjsip_event e; … … 1045 1105 1046 1106 /* Calculate hashed key value. */ 1107 PJ_TODO(OPTIMIZE_TSX_BY_PRECALCULATING_HASHED_KEY_VALUE); 1108 /* 1109 blp: somehow this yields different hashed value!! 1110 1047 1111 tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, 1048 1112 tsx->transaction_key.slen); 1113 */ 1049 1114 1050 1115 PJ_LOG(6, (tsx->obj_name, "tsx_key=%.*s", tsx->transaction_key.slen, … … 1159 1224 1160 1225 /* Calculate hashed key value. */ 1226 PJ_TODO(OPTIMIZE_TSX_BY_PRECALCULATING_HASHED_KEY_VALUE); 1227 /* 1228 blp: somehow this yields different hashed value!! 1229 1161 1230 tsx->hashed_key = pj_hash_calc(0, tsx->transaction_key.ptr, 1162 1231 tsx->transaction_key.slen); 1232 */ 1163 1233 1164 1234 /* Duplicate branch parameter for transaction. */ -
pjproject/trunk/pjsip/src/pjsip/sip_ua_layer.c
r127 r139 169 169 dlg = tsx->mod_data[mod_ua.mod.id]; 170 170 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; 173 176 174 177 /* Hand over the event to the dialog. */ … … 205 208 { 206 209 return &mod_ua.mod; 210 } 211 212 213 /* 214 * Get the endpoint where this UA is currently registered. 215 */ 216 PJ_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; 207 221 } 208 222 … … 461 475 pjsip_dialog *dlg; 462 476 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 463 481 /* Lock user agent before looking up the dialog hash table. */ 464 482 pj_mutex_lock(mod_ua.mutex); … … 467 485 dlg_set = find_dlg_set_for_msg(rdata); 468 486 469 /* Bail out if dialog is not found. */ 487 /* If dialog is not found, respond with 481 (Call/Transaction 488 * Does Not Exist). 489 */ 470 490 if (dlg_set == NULL) { 471 /* Not ours. */491 /* Unable to find dialog. */ 472 492 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; 474 498 } 475 499 … … 531 555 /* Check if transaction is present. */ 532 556 tsx = pjsip_rdata_get_tsx(rdata); 533 if ( !tsx) {557 if (tsx) { 534 558 /* Check if dialog is present in the transaction. */ 535 559 dlg = pjsip_tsx_get_dlg(tsx); -
pjproject/trunk/pjsip/src/pjsip/sip_util.c
r131 r139 44 44 "TRANSPORT_ERROR", 45 45 "TSX_STATE", 46 "RX_2XX_RESPONSE",47 "RX_ACK",48 "DISCARD_MSG",49 46 "USER", 50 "BEFORE_TX",51 47 }; 52 48 -
pjproject/trunk/pjsip/src/pjsua/getopt.c
r65 r139 36 36 #include "config.h" 37 37 #endif 38 39 #include <pj/string.h> 38 40 39 41 #ifndef HAVE_GETOPT_LONG -
pjproject/trunk/pjsip/src/pjsua/main.c
r119 r139 17 17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 18 18 */ 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 30 static pjsip_inv_session *inv_session; 31 32 /* 33 * Notify UI when invite state has changed. 36 34 */ 37 #if PJ_HAS_THREADS 38 # define WORKER_COUNT 1 35 void 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 66 static 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 76 static 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); 39 93 #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); 277 127 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; 328 158 } 329 159 } 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 161 on_exit: 162 ; 163 } 164 165 static 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 180 static 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 194 static 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 214 int 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. 357 228 */ 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. 368 235 */ 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 1037 256 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, ®c_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.