Changeset 1469
- Timestamp:
- Oct 3, 2007 6:28:49 PM (17 years ago)
- Location:
- pjproject/trunk
- Files:
-
- 1 added
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/build.symbian/pjsip_uaU.def
r1463 r1469 1 1 EXPORTS 2 2 pjsip_100rel_attach @ 1 NONAME 3 pjsip_100rel_init_module @ 2 NONAME 4 pjsip_100rel_tx_response @ 3 NONAME 5 pjsip_create_sdp_body @ 4 NONAME 6 pjsip_dlg_get_inv_session @ 5 NONAME 7 pjsip_get_refer_method @ 6 NONAME 8 pjsip_inv_answer @ 7 NONAME 9 pjsip_inv_create_uac @ 8 NONAME 10 pjsip_inv_create_uas @ 9 NONAME 11 pjsip_inv_end_session @ 10 NONAME 12 pjsip_inv_initial_answer @ 11 NONAME 13 pjsip_inv_invite @ 12 NONAME 14 pjsip_inv_reinvite @ 13 NONAME 15 pjsip_inv_send_msg @ 14 NONAME 16 pjsip_inv_set_sdp_answer @ 15 NONAME 17 pjsip_inv_state_name @ 16 NONAME 18 pjsip_inv_terminate @ 17 NONAME 19 pjsip_inv_update @ 18 NONAME 20 pjsip_inv_usage_init @ 19 NONAME 21 pjsip_inv_usage_instance @ 20 NONAME 22 pjsip_inv_verify_request @ 21 NONAME 23 pjsip_refer_method @ 22 NONAME 24 pjsip_regc_add_headers @ 23 NONAME 25 pjsip_regc_create @ 24 NONAME 26 pjsip_regc_destroy @ 25 NONAME 27 pjsip_regc_get_info @ 26 NONAME 28 pjsip_regc_get_pool @ 27 NONAME 29 pjsip_regc_init @ 28 NONAME 30 pjsip_regc_register @ 29 NONAME 31 pjsip_regc_send @ 30 NONAME 32 pjsip_regc_set_credentials @ 31 NONAME 33 pjsip_regc_set_route_set @ 32 NONAME 34 pjsip_regc_set_transport @ 33 NONAME 35 pjsip_regc_unregister @ 34 NONAME 36 pjsip_regc_unregister_all @ 35 NONAME 37 pjsip_regc_update_contact @ 36 NONAME 38 pjsip_regc_update_expires @ 37 NONAME 39 pjsip_replaces_hdr_create @ 38 NONAME 40 pjsip_replaces_init_module @ 39 NONAME 41 pjsip_replaces_verify_request @ 40 NONAME 42 pjsip_xfer_accept @ 41 NONAME 43 pjsip_xfer_create_uac @ 42 NONAME 44 pjsip_xfer_create_uas @ 43 NONAME 45 pjsip_xfer_current_notify @ 44 NONAME 46 pjsip_xfer_init_module @ 45 NONAME 47 pjsip_xfer_initiate @ 46 NONAME 48 pjsip_xfer_notify @ 47 NONAME 49 pjsip_xfer_send_request @ 48 NONAME 3 pjsip_100rel_create_prack @ 2 NONAME 4 pjsip_100rel_end_session @ 3 NONAME 5 pjsip_100rel_init_module @ 4 NONAME 6 pjsip_100rel_is_reliable @ 5 NONAME 7 pjsip_100rel_on_rx_prack @ 6 NONAME 8 pjsip_100rel_send_prack @ 7 NONAME 9 pjsip_100rel_tx_response @ 8 NONAME 10 pjsip_create_sdp_body @ 9 NONAME 11 pjsip_dlg_get_inv_session @ 10 NONAME 12 pjsip_get_prack_method @ 11 NONAME 13 pjsip_get_refer_method @ 12 NONAME 14 pjsip_inv_answer @ 13 NONAME 15 pjsip_inv_create_uac @ 14 NONAME 16 pjsip_inv_create_uas @ 15 NONAME 17 pjsip_inv_end_session @ 16 NONAME 18 pjsip_inv_initial_answer @ 17 NONAME 19 pjsip_inv_invite @ 18 NONAME 20 pjsip_inv_reinvite @ 19 NONAME 21 pjsip_inv_send_msg @ 20 NONAME 22 pjsip_inv_set_sdp_answer @ 21 NONAME 23 pjsip_inv_state_name @ 22 NONAME 24 pjsip_inv_terminate @ 23 NONAME 25 pjsip_inv_update @ 24 NONAME 26 pjsip_inv_usage_init @ 25 NONAME 27 pjsip_inv_usage_instance @ 26 NONAME 28 pjsip_inv_verify_request @ 27 NONAME 29 pjsip_prack_method @ 28 NONAME 30 pjsip_refer_method @ 29 NONAME 31 pjsip_regc_add_headers @ 30 NONAME 32 pjsip_regc_create @ 31 NONAME 33 pjsip_regc_destroy @ 32 NONAME 34 pjsip_regc_get_info @ 33 NONAME 35 pjsip_regc_get_pool @ 34 NONAME 36 pjsip_regc_init @ 35 NONAME 37 pjsip_regc_register @ 36 NONAME 38 pjsip_regc_send @ 37 NONAME 39 pjsip_regc_set_credentials @ 38 NONAME 40 pjsip_regc_set_route_set @ 39 NONAME 41 pjsip_regc_set_transport @ 40 NONAME 42 pjsip_regc_unregister @ 41 NONAME 43 pjsip_regc_unregister_all @ 42 NONAME 44 pjsip_regc_update_contact @ 43 NONAME 45 pjsip_regc_update_expires @ 44 NONAME 46 pjsip_replaces_hdr_create @ 45 NONAME 47 pjsip_replaces_init_module @ 46 NONAME 48 pjsip_replaces_verify_request @ 47 NONAME 49 pjsip_xfer_accept @ 48 NONAME 50 pjsip_xfer_create_uac @ 49 NONAME 51 pjsip_xfer_create_uas @ 50 NONAME 52 pjsip_xfer_current_notify @ 51 NONAME 53 pjsip_xfer_init_module @ 52 NONAME 54 pjsip_xfer_initiate @ 53 NONAME 55 pjsip_xfer_notify @ 54 NONAME 56 pjsip_xfer_send_request @ 55 NONAME -
pjproject/trunk/pjlib/include/pj/errno.h
r1405 r1469 305 305 */ 306 306 #define PJ_ETOOSMALL (PJ_ERRNO_START_STATUS + 19)/* 70019 */ 307 307 /** 308 * @hideinitializer 309 * Ignored 310 */ 311 #define PJ_EIGNORED (PJ_ERRNO_START_STATUS + 20)/* 70020 */ 308 312 309 313 /** @} */ /* pj_errnum */ -
pjproject/trunk/pjlib/src/pj/errno.c
r1405 r1469 71 71 PJ_BUILD_ERR(PJ_ERESOLVE, "gethostbyname() has returned error"), 72 72 PJ_BUILD_ERR(PJ_ETOOSMALL, "Size is too short"), 73 PJ_BUILD_ERR(PJ_EIGNORED, "Ignored"), 73 74 }; 74 75 #endif /* PJ_HAS_ERROR_STRING */ -
pjproject/trunk/pjsip/build/Makefile
r1463 r1469 89 89 transport_test.o transport_udp_test.o \ 90 90 tsx_basic_test.o tsx_bench.o tsx_uac_test.o \ 91 tsx_uas_test.o txdata_test.o uri_test.o 91 tsx_uas_test.o txdata_test.o uri_test.o \ 92 inv_offer_answer_test.o 92 93 export TEST_CFLAGS += $(_CFLAGS) 93 94 export TEST_LDFLAGS += $(PJ_LDFLAGS) $(PJ_LDLIBS) $(LDFLAGS) -
pjproject/trunk/pjsip/build/test_pjsip.dsp
r1102 r1469 98 98 # Begin Source File 99 99 100 SOURCE="..\src\test-pjsip\inv_offer_answer_test.c" 101 # End Source File 102 # Begin Source File 103 100 104 SOURCE="..\src\test-pjsip\main.c" 101 105 # End Source File -
pjproject/trunk/pjsip/build/test_pjsip.vcproj
r1177 r1469 45 45 Optimization="2" 46 46 InlineFunctionExpansion="2" 47 AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include "47 AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include" 48 48 PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;PJ_WIN32;PJ_M_I386" 49 49 StringPooling="true" … … 140 140 Name="VCCLCompilerTool" 141 141 Optimization="0" 142 AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include "142 AdditionalIncludeDirectories="../include,../../pjlib/include,../../pjlib-util/include,../../pjmedia/include" 143 143 PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;PJ_WIN32;PJ_M_I386" 144 144 MinimalRebuild="true" … … 256 256 </File> 257 257 <File 258 RelativePath="..\src\test-pjsip\inv_offer_answer_test.c" 259 > 260 </File> 261 <File 258 262 RelativePath="..\src\test-pjsip\main.c" 259 263 > -
pjproject/trunk/pjsip/include/pjsip-ua/sip_100rel.h
r1463 r1469 46 46 * \subsection pjsip_100rel_init Initializing 100rel Module 47 47 * 48 * \a PRACK and \a 100rel extension support is built into the library when 49 * #PJSIP_HAS_100REL macro is enabled. The default is yes. Application can 50 * set this macro to zero if it does not wish to support reliable provisional 51 * response extension. 52 * 53 * Application must also explicitly initialize 100rel module by calling 48 * Application must explicitly initialize 100rel module by calling 54 49 * #pjsip_100rel_init_module() in application initialization function. 55 50 * … … 60 55 * 61 56 * For UAC, \a 100rel support will be enabled in the session if \a 100rel 62 * support is enabled in the library ( with #PJSIP_HAS_100REL macro).57 * support is enabled in the library (default is yes). 63 58 * Outgoing INVITE request will include \a 100rel tag in \a Supported 64 59 * header and \a PRACK method in \a Allow header. When callee endpoint … … 87 82 unsigned options = 0; 88 83 89 #if PJSIP_HAS_100REL90 84 options |= PJSIP_INV_SUPPORT_100REL; 91 #endif92 85 93 86 status = pjsip_inv_verify_request(rdata, &options, answer, NULL, … … 130 123 PJ_BEGIN_DECL 131 124 125 126 /** 127 * PRACK method constant. 128 * @see pjsip_get_prack_method() 129 */ 130 PJ_DECL_DATA(const pjsip_method) pjsip_prack_method; 131 132 133 /** 134 * Get #pjsip_invite_method constant. 135 */ 136 PJ_DECL(const pjsip_method*) pjsip_get_prack_method(void); 137 138 132 139 /** 133 140 * Initialize 100rel module. This function must be called once during … … 139 146 */ 140 147 PJ_DECL(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt); 148 141 149 142 150 /** … … 150 158 */ 151 159 PJ_DECL(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv); 160 161 162 /** 163 * Check if incoming response has reliable provisional response feature. 164 * 165 * @param rdata Receive data buffer containing the response. 166 * 167 * @return PJ_TRUE if the provisional response is reliable. 168 */ 169 PJ_DECL(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata); 170 171 172 /** 173 * Create PRACK request for the incoming reliable provisional response. 174 * Note that PRACK request MUST be sent using #pjsip_100rel_send_prack(). 175 * 176 * @param inv The invite session. 177 * @param rdata The incoming reliable provisional response. 178 * @param p_tdata Upon return, it will be initialized with the 179 * PRACK request. 180 * 181 * @return PJ_SUCCESS on successful. 182 */ 183 PJ_DECL(pj_status_t) pjsip_100rel_create_prack(pjsip_inv_session *inv, 184 pjsip_rx_data *rdata, 185 pjsip_tx_data **p_tdata); 186 187 /** 188 * Send PRACK request. 189 * 190 * @param inv The invite session. 191 * @param tdata The PRACK request. 192 * 193 * @return PJ_SUCCESS on successful. 194 */ 195 PJ_DECL(pj_status_t) pjsip_100rel_send_prack(pjsip_inv_session *inv, 196 pjsip_tx_data *tdata); 197 198 199 /** 200 * Handle incoming PRACK request. 201 * 202 * @param inv The invite session. 203 * @param rdata Incoming PRACK request. 204 * 205 * @return PJ_SUCCESS on successful. 206 */ 207 PJ_DECL(pj_status_t) pjsip_100rel_on_rx_prack(pjsip_inv_session *inv, 208 pjsip_rx_data *rdata); 209 152 210 153 211 /** … … 167 225 168 226 227 /** 228 * Notify 100rel module that the invite session has been disconnected. 229 * 230 * @param inv The invite session. 231 * 232 * @return PJ_SUCCESS on successful. 233 */ 234 PJ_DECL(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv); 235 236 169 237 PJ_END_DECL 170 238 -
pjproject/trunk/pjsip/include/pjsip-ua/sip_inv.h
r1463 r1469 254 254 pjsip_transaction *invite_tsx; /**< 1st invite tsx. */ 255 255 pjsip_tx_data *last_answer; /**< Last INVITE resp. */ 256 pjsip_tx_data *last_ack; /**< Last ACK request */ 257 pj_int32_t last_ack_cseq; /**< CSeq of last ACK */ 256 258 void *mod_data[PJSIP_MAX_MODULE];/**< Modules data. */ 257 259 }; … … 562 564 563 565 /** 564 * Create an UPDATE request .566 * Create an UPDATE request to initiate new SDP offer. 565 567 * 566 568 * @param inv The invite session. … … 569 571 * contact, it can specify the new contact in this 570 572 * argument; otherwise this argument must be NULL. 571 * @param new_offer Application MAY initiate a new SDP offer/answer 572 * session in the request when there is no pending answer 573 * to be sent or received. It can detect this condition 574 * by observing the state of the SDP negotiator of the 575 * invite session. If new offer should be sent to remote, 576 * the offer must be specified in this argument; otherwise 577 * this argument must be NULL. 573 * @param offer Offer to be sent to remote. This argument is 574 * mandatory. 578 575 * @param p_tdata Pointer to receive the UPDATE request message to 579 576 * be created. … … 585 582 PJ_DECL(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, 586 583 const pj_str_t *new_contact, 587 const pjmedia_sdp_session * new_offer,584 const pjmedia_sdp_session *offer, 588 585 pjsip_tx_data **p_tdata ); 589 586 -
pjproject/trunk/pjsip/include/pjsip/sip_config.h
r1463 r1469 66 66 67 67 /** 68 * Specify whether support for reliable provisional response (100rel, PRACK)69 * should be built in the library.70 *71 * Default: 172 */73 #ifndef PJSIP_HAS_100REL74 # define PJSIP_HAS_100REL 175 #endif76 77 78 /**79 68 * Specify maximum transaction count in transaction hash table. 80 69 * Default value is 16*1024 -
pjproject/trunk/pjsip/src/pjsip-ua/sip_100rel.c
r1467 r1469 29 29 #include <pj/rand.h> 30 30 31 #if defined(PJSIP_HAS_100REL) && PJSIP_HAS_100REL!=032 33 31 #define THIS_FILE "sip_100rel.c" 34 32 33 /* PRACK method */ 34 PJ_DEF_DATA(const pjsip_method) pjsip_prack_method = 35 { 36 PJSIP_OTHER_METHOD, 37 { "PRACK", 5 } 38 }; 39 35 40 typedef struct dlg_data dlg_data; 36 41 … … 39 44 */ 40 45 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt); 41 static void mod_100rel_on_tsx_state(pjsip_transaction*, pjsip_event*);42 46 43 47 static void handle_incoming_prack(dlg_data *dd, pjsip_transaction *tsx, … … 48 52 struct pj_timer_entry *entry); 49 53 50 51 /* PRACK method */52 const pjsip_method pjsip_prack_method =53 {54 PJSIP_OTHER_METHOD,55 { "PRACK", 5 }56 };57 54 58 55 const pj_str_t tag_100rel = { "100rel", 6 }; … … 81 78 NULL, /* on_tx_request. */ 82 79 NULL, /* on_tx_response() */ 83 &mod_100rel_on_tsx_state,/* on_tsx_state() */80 NULL, /* on_tsx_state() */ 84 81 } 85 82 … … 133 130 static pj_status_t mod_100rel_load(pjsip_endpoint *endpt) 134 131 { 135 mod_100rel.endpt = endpt; 136 pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 137 PJSIP_H_ALLOW, NULL, 138 1, &pjsip_prack_method.name); 139 pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 140 PJSIP_H_SUPPORTED, NULL, 141 1, &tag_100rel); 142 132 mod_100rel.endpt = endpt; 133 pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 134 PJSIP_H_ALLOW, NULL, 135 1, &pjsip_prack_method.name); 136 pjsip_endpt_add_capability(endpt, &mod_100rel.mod, 137 PJSIP_H_SUPPORTED, NULL, 138 1, &tag_100rel); 139 140 return PJ_SUCCESS; 141 } 142 143 static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg) 144 { 145 pjsip_require_hdr *hreq; 146 147 hreq = (pjsip_require_hdr*) 148 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL); 149 150 while (hreq) { 151 unsigned i; 152 for (i=0; i<hreq->count; ++i) { 153 if (!pj_stricmp(&hreq->values[i], &tag_100rel)) { 154 return hreq; 155 } 156 } 157 158 if ((void*)hreq->next == (void*)&msg->hdr) 159 return NULL; 160 161 hreq = (pjsip_require_hdr*) 162 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next); 163 164 } 165 166 return NULL; 167 } 168 169 170 /* 171 * Get PRACK method constant. 172 */ 173 PJ_DEF(const pjsip_method*) pjsip_get_prack_method(void) 174 { 175 return &pjsip_prack_method; 176 } 177 178 179 /* 180 * init module 181 */ 182 PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt) 183 { 184 if (mod_100rel.mod.id != -1) 143 185 return PJ_SUCCESS; 144 } 145 146 static pjsip_require_hdr *find_req_hdr(pjsip_msg *msg) 147 { 148 pjsip_require_hdr *hreq; 149 150 hreq = (pjsip_require_hdr*) 151 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, NULL); 152 153 while (hreq) { 154 unsigned i; 155 for (i=0; i<hreq->count; ++i) { 156 if (!pj_stricmp(&hreq->values[i], &tag_100rel)) { 157 return hreq; 158 } 159 } 160 161 if ((void*)hreq->next == (void*)&msg->hdr) 162 return NULL; 163 164 hreq = (pjsip_require_hdr*) 165 pjsip_msg_find_hdr(msg, PJSIP_H_REQUIRE, hreq->next); 166 167 } 168 169 return NULL; 170 } 171 172 static void mod_100rel_on_tsx_state(pjsip_transaction *tsx, pjsip_event *e) 173 { 174 pjsip_dialog *dlg; 175 dlg_data *dd; 176 177 dlg = pjsip_tsx_get_dlg(tsx); 178 if (!dlg) 179 return; 180 181 dd = (dlg_data*) dlg->mod_data[mod_100rel.mod.id]; 182 if (!dd) 183 return; 184 185 if (tsx->role == PJSIP_ROLE_UAS && 186 tsx->state == PJSIP_TSX_STATE_TRYING && 187 pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) 188 { 189 /* 190 * Handle incoming PRACK request. 191 */ 192 handle_incoming_prack(dd, tsx, e); 193 194 } else if (tsx->role == PJSIP_ROLE_UAC && 195 tsx->method.id == PJSIP_INVITE_METHOD && 196 e->type == PJSIP_EVENT_TSX_STATE && 197 e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 198 e->body.tsx_state.src.rdata->msg_info.msg->line.status.code > 100 && 199 e->body.tsx_state.src.rdata->msg_info.msg->line.status.code < 200 && 200 e->body.tsx_state.src.rdata->msg_info.require != NULL) 201 { 202 /* 203 * Handle incoming provisional response which wants to 204 * be PRACK-ed 205 */ 206 207 if (find_req_hdr(e->body.tsx_state.src.rdata->msg_info.msg)) { 208 /* Received provisional response which needs to be 209 * PRACK-ed. 210 */ 211 handle_incoming_response(dd, tsx, e); 212 } 213 214 } else if (tsx->role == PJSIP_ROLE_UAC && 215 tsx->state == PJSIP_TSX_STATE_COMPLETED && 216 pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) 217 { 218 /* 219 * Handle the status of outgoing PRACK request. 220 */ 221 if (tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST || 222 tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT || 223 tsx->status_code == PJSIP_SC_TSX_TIMEOUT || 224 tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR) 225 { 226 /* These are fatal errors which should terminate 227 * the session AND dialog! 228 */ 229 PJ_TODO(TERMINATE_SESSION_ON_481); 230 } 231 232 } else if (tsx == dd->inv->invite_tsx && 233 tsx->role == PJSIP_ROLE_UAS && 234 tsx->state == PJSIP_TSX_STATE_TERMINATED) 235 { 236 /* Make sure we don't have pending transmission */ 237 if (dd->uas_state) { 238 pj_assert(!dd->uas_state->retransmit_timer.id); 239 pj_assert(pj_list_empty(&dd->uas_state->tx_data_list)); 240 } 241 } 242 } 186 187 return pjsip_endpt_register_module(endpt, &mod_100rel.mod); 188 } 189 190 191 /* 192 * API: attach 100rel support in invite session. Called by 193 * sip_inv.c 194 */ 195 PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv) 196 { 197 dlg_data *dd; 198 199 /* Check that 100rel module has been initialized */ 200 PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP); 201 202 /* Create and attach as dialog usage */ 203 dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data); 204 dd->inv = inv; 205 pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd); 206 207 PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached")); 208 209 return PJ_SUCCESS; 210 } 211 212 213 /* 214 * Check if incoming response has reliable provisional response feature. 215 */ 216 PJ_DEF(pj_bool_t) pjsip_100rel_is_reliable(pjsip_rx_data *rdata) 217 { 218 pjsip_msg *msg = rdata->msg_info.msg; 219 220 PJ_ASSERT_RETURN(msg->type == PJSIP_RESPONSE_MSG, PJ_FALSE); 221 222 return msg->line.status.code > 100 && msg->line.status.code < 200 && 223 rdata->msg_info.require != NULL && 224 find_req_hdr(msg) != NULL; 225 } 226 227 228 /* 229 * Create PRACK request for the incoming reliable provisional response. 230 */ 231 PJ_DEF(pj_status_t) pjsip_100rel_create_prack( pjsip_inv_session *inv, 232 pjsip_rx_data *rdata, 233 pjsip_tx_data **p_tdata) 234 { 235 dlg_data *dd; 236 pjsip_transaction *tsx; 237 pjsip_msg *msg; 238 pjsip_generic_string_hdr *rseq_hdr; 239 pjsip_generic_string_hdr *rack_hdr; 240 unsigned rseq; 241 pj_str_t rack; 242 char rack_buf[80]; 243 pjsip_tx_data *tdata; 244 pj_status_t status; 245 246 *p_tdata = NULL; 247 248 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 249 PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED); 250 251 tsx = pjsip_rdata_get_tsx(rdata); 252 msg = rdata->msg_info.msg; 253 254 /* Check our assumptions */ 255 pj_assert( tsx->role == PJSIP_ROLE_UAC && 256 tsx->method.id == PJSIP_INVITE_METHOD && 257 msg->line.status.code > 100 && 258 msg->line.status.code < 200); 259 260 261 /* Get the RSeq header */ 262 rseq_hdr = (pjsip_generic_string_hdr*) 263 pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); 264 if (rseq_hdr == NULL) { 265 PJ_LOG(4,(dd->inv->dlg->obj_name, 266 "Ignoring provisional response with no RSeq header")); 267 return PJSIP_EMISSINGHDR; 268 } 269 rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); 270 271 /* Create new UAC state if we don't have one */ 272 if (dd->uac_state == NULL) { 273 dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, 274 uac_state_t); 275 dd->uac_state->cseq = rdata->msg_info.cseq->cseq; 276 dd->uac_state->rseq = rseq - 1; 277 } 278 279 /* If this is from new INVITE transaction, reset UAC state */ 280 if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) { 281 dd->uac_state->cseq = rdata->msg_info.cseq->cseq; 282 dd->uac_state->rseq = rseq - 1; 283 } 284 285 /* Ignore provisional response retransmission */ 286 if (rseq <= dd->uac_state->rseq) { 287 /* This should have been handled before */ 288 return PJ_EIGNORED; 289 290 /* Ignore provisional response with out-of-order RSeq */ 291 } else if (rseq != dd->uac_state->rseq + 1) { 292 PJ_LOG(4,(dd->inv->dlg->obj_name, 293 "Ignoring provisional response because RSeq jump " 294 "(expecting %u, got %u)", 295 dd->uac_state->rseq+1, rseq)); 296 return PJ_EIGNORED; 297 } 298 299 /* Update our RSeq */ 300 dd->uac_state->rseq = rseq; 301 302 /* Create PRACK */ 303 status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, 304 -1, &tdata); 305 if (status != PJ_SUCCESS) 306 return status; 307 308 /* Create RAck header */ 309 rack.ptr = rack_buf; 310 rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), 311 "%u %u %.*s", 312 rseq, rdata->msg_info.cseq->cseq, 313 (int)tsx->method.name.slen, 314 tsx->method.name.ptr); 315 rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack); 316 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr); 317 318 /* Done */ 319 *p_tdata = tdata; 320 321 return PJ_SUCCESS; 322 } 323 324 325 /* 326 * Send PRACK request. 327 */ 328 PJ_DEF(pj_status_t) pjsip_100rel_send_prack( pjsip_inv_session *inv, 329 pjsip_tx_data *tdata) 330 { 331 dlg_data *dd; 332 333 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 334 PJ_ASSERT_ON_FAIL(dd != NULL, 335 {pjsip_tx_data_dec_ref(tdata); return PJSIP_ENOTINITIALIZED; }); 336 337 return pjsip_dlg_send_request(inv->dlg, tdata, 338 mod_100rel.mod.id, (void*) dd); 339 340 } 341 342 343 /* 344 * Notify 100rel module that the invite session has been disconnected. 345 */ 346 PJ_DEF(pj_status_t) pjsip_100rel_end_session(pjsip_inv_session *inv) 347 { 348 dlg_data *dd; 349 350 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 351 if (!dd) 352 return PJ_SUCCESS; 353 354 /* Make sure we don't have pending transmission */ 355 if (dd->uas_state) { 356 pj_assert(!dd->uas_state->retransmit_timer.id); 357 pj_assert(pj_list_empty(&dd->uas_state->tx_data_list)); 358 } 359 360 return PJ_SUCCESS; 361 } 362 243 363 244 364 static void parse_rack(const pj_str_t *rack, … … 246 366 pj_str_t *p_method) 247 367 { 248 const char *p = rack->ptr, *end = p + rack->slen; 249 pj_str_t token; 250 251 token.ptr = (char*)p; 252 while (p < end && pj_isdigit(*p)) 253 ++p; 254 token.slen = p - token.ptr; 255 *p_rseq = pj_strtoul(&token); 256 368 const char *p = rack->ptr, *end = p + rack->slen; 369 pj_str_t token; 370 371 token.ptr = (char*)p; 372 while (p < end && pj_isdigit(*p)) 257 373 ++p; 258 token.ptr = (char*)p;259 while (p < end && pj_isdigit(*p)) 260 ++p; 261 token.slen = p - token.ptr;262 *p_seq = pj_strtoul(&token);263 374 token.slen = p - token.ptr; 375 *p_rseq = pj_strtoul(&token); 376 377 ++p; 378 token.ptr = (char*)p; 379 while (p < end && pj_isdigit(*p)) 264 380 ++p; 265 if (p < end) { 266 p_method->ptr = (char*)p; 267 p_method->slen = end - p; 268 } else { 269 p_method->ptr = NULL; 270 p_method->slen = 0; 271 } 381 token.slen = p - token.ptr; 382 *p_seq = pj_strtoul(&token); 383 384 ++p; 385 if (p < end) { 386 p_method->ptr = (char*)p; 387 p_method->slen = end - p; 388 } else { 389 p_method->ptr = NULL; 390 p_method->slen = 0; 391 } 272 392 } 273 393 … … 275 395 static void clear_all_responses(dlg_data *dd) 276 396 { 277 tx_data_list_t *tl; 278 279 tl = dd->uas_state->tx_data_list.next; 280 while (tl != &dd->uas_state->tx_data_list) { 281 pjsip_tx_data_dec_ref(tl->tdata); 282 tl = tl->next; 397 tx_data_list_t *tl; 398 399 tl = dd->uas_state->tx_data_list.next; 400 while (tl != &dd->uas_state->tx_data_list) { 401 pjsip_tx_data_dec_ref(tl->tdata); 402 tl = tl->next; 403 } 404 pj_list_init(&dd->uas_state->tx_data_list); 405 } 406 407 408 /* 409 * Handle incoming PRACK request. 410 */ 411 PJ_DEF(pj_status_t) pjsip_100rel_on_rx_prack( pjsip_inv_session *inv, 412 pjsip_rx_data *rdata) 413 { 414 dlg_data *dd; 415 pjsip_transaction *tsx; 416 pjsip_msg *msg; 417 pjsip_generic_string_hdr *rack_hdr; 418 pjsip_tx_data *tdata; 419 pj_uint32_t rseq; 420 pj_int32_t cseq; 421 pj_str_t method; 422 pj_status_t status; 423 424 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 425 PJ_ASSERT_RETURN(dd != NULL, PJSIP_ENOTINITIALIZED); 426 427 tsx = pjsip_rdata_get_tsx(rdata); 428 pj_assert(tsx != NULL); 429 430 msg = rdata->msg_info.msg; 431 432 /* Always reply with 200/OK for PRACK */ 433 status = pjsip_dlg_create_response(inv->dlg, rdata, 200, NULL, &tdata); 434 if (status == PJ_SUCCESS) { 435 status = pjsip_dlg_send_response(inv->dlg, tsx, tdata); 436 } 437 438 /* Ignore if we don't have pending transmission */ 439 if (dd->uas_state == NULL || pj_list_empty(&dd->uas_state->tx_data_list)) { 440 PJ_LOG(4,(dd->inv->dlg->obj_name, 441 "PRACK ignored - no pending response")); 442 return PJ_EIGNORED; 443 } 444 445 /* Find RAck header */ 446 rack_hdr = (pjsip_generic_string_hdr*) 447 pjsip_msg_find_hdr_by_name(msg, &RACK, NULL); 448 if (!rack_hdr) { 449 /* RAck header not found */ 450 PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header")); 451 return PJSIP_EMISSINGHDR; 452 } 453 454 /* Parse RAck header */ 455 parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method); 456 457 458 /* Match RAck against outgoing transmission */ 459 if (rseq == dd->uas_state->tx_data_list.next->rseq && 460 cseq == dd->uas_state->cseq) 461 { 462 /* 463 * Yes this PRACK matches outgoing transmission. 464 */ 465 tx_data_list_t *tl = dd->uas_state->tx_data_list.next; 466 467 if (dd->uas_state->retransmit_timer.id) { 468 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 469 &dd->uas_state->retransmit_timer); 470 dd->uas_state->retransmit_timer.id = PJ_FALSE; 283 471 } 284 pj_list_init(&dd->uas_state->tx_data_list); 285 } 286 287 288 static void handle_incoming_prack(dlg_data *dd, pjsip_transaction *tsx, 289 pjsip_event *e) 290 { 291 pjsip_rx_data *rdata; 292 pjsip_msg *msg; 293 pjsip_generic_string_hdr *rack_hdr; 294 pjsip_tx_data *tdata; 295 pj_uint32_t rseq; 296 pj_int32_t cseq; 297 pj_str_t method; 298 pj_status_t status; 299 300 301 rdata = e->body.tsx_state.src.rdata; 302 msg = rdata->msg_info.msg; 303 304 /* Always reply with 200/OK for PRACK */ 305 status = pjsip_endpt_create_response(tsx->endpt, rdata, 306 200, NULL, &tdata); 307 if (status == PJ_SUCCESS) 308 pjsip_tsx_send_msg(tsx, tdata); 309 310 /* Ignore if we don't have pending transmission */ 311 if (dd->uas_state == NULL || 312 pj_list_empty(&dd->uas_state->tx_data_list)) 313 { 314 PJ_LOG(4,(dd->inv->dlg->obj_name, 315 "PRACK ignored - no pending response")); 316 return; 472 473 /* Remove from the list */ 474 if (tl != &dd->uas_state->tx_data_list) { 475 pj_list_erase(tl); 476 477 /* Destroy the response */ 478 pjsip_tx_data_dec_ref(tl->tdata); 317 479 } 318 480 319 /* Find RAck header */ 320 rack_hdr = (pjsip_generic_string_hdr*) 321 pjsip_msg_find_hdr_by_name(msg, &RACK, NULL); 322 if (!rack_hdr) { 323 /* RAck header not found */ 324 PJ_LOG(4,(dd->inv->dlg->obj_name, "No RAck header")); 325 return; 481 /* Schedule next packet */ 482 dd->uas_state->retransmit_count = 0; 483 if (!pj_list_empty(&dd->uas_state->tx_data_list)) { 484 on_retransmit(NULL, &dd->uas_state->retransmit_timer); 326 485 } 327 parse_rack(&rack_hdr->hvalue, &rseq, &cseq, &method); 328 329 /* Match RAck against outgoing transmission */ 330 if (rseq == dd->uas_state->tx_data_list.next->rseq && 331 cseq == dd->uas_state->cseq) 332 { 333 tx_data_list_t *tl = dd->uas_state->tx_data_list.next; 334 335 /* Yes it match! */ 336 if (dd->uas_state->retransmit_timer.id) { 337 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 338 &dd->uas_state->retransmit_timer); 339 dd->uas_state->retransmit_timer.id = PJ_FALSE; 340 } 341 342 /* Remove from the list */ 343 if (tl != &dd->uas_state->tx_data_list) { 344 pj_list_erase(tl); 345 346 /* Destroy the response */ 347 pjsip_tx_data_dec_ref(tl->tdata); 348 } 349 350 /* Schedule next packet */ 351 dd->uas_state->retransmit_count = 0; 352 if (!pj_list_empty(&dd->uas_state->tx_data_list)) { 353 on_retransmit(NULL, &dd->uas_state->retransmit_timer); 354 } 355 356 } else { 357 /* No it doesn't match */ 358 PJ_LOG(4,(dd->inv->dlg->obj_name, 359 "Rx PRACK with no matching reliable response")); 360 } 361 } 362 363 364 /* 365 * Handle incoming provisional response with 100rel requirement. 366 * In this case we shall transmit PRACK request. 367 */ 368 static void handle_incoming_response(dlg_data *dd, pjsip_transaction *tsx, 369 pjsip_event *e) 370 { 371 pjsip_rx_data *rdata; 372 pjsip_msg *msg; 373 pjsip_generic_string_hdr *rseq_hdr; 374 pjsip_generic_string_hdr *rack_hdr; 375 unsigned rseq; 376 pj_str_t rack; 377 char rack_buf[80]; 378 pjsip_tx_data *tdata; 379 pj_status_t status; 380 381 rdata = e->body.tsx_state.src.rdata; 382 msg = rdata->msg_info.msg; 383 384 /* Check our assumptions */ 385 pj_assert( tsx->role == PJSIP_ROLE_UAC && 386 tsx->method.id == PJSIP_INVITE_METHOD && 387 e->type == PJSIP_EVENT_TSX_STATE && 388 e->body.tsx_state.type == PJSIP_EVENT_RX_MSG && 389 msg->line.status.code > 100 && 390 msg->line.status.code < 200); 391 392 393 /* Get the RSeq header */ 394 rseq_hdr = (pjsip_generic_string_hdr*) 395 pjsip_msg_find_hdr_by_name(msg, &RSEQ, NULL); 396 if (rseq_hdr == NULL) { 397 PJ_LOG(4,(dd->inv->dlg->obj_name, 398 "Ignoring provisional response with no RSeq header")); 399 return; 400 } 401 rseq = (pj_uint32_t) pj_strtoul(&rseq_hdr->hvalue); 402 403 /* Create new UAC state if we don't have one */ 404 if (dd->uac_state == NULL) { 405 dd->uac_state = PJ_POOL_ZALLOC_T(dd->inv->dlg->pool, 406 uac_state_t); 407 dd->uac_state->cseq = rdata->msg_info.cseq->cseq; 408 dd->uac_state->rseq = rseq - 1; 409 } 410 411 /* If this is from new INVITE transaction, reset UAC state */ 412 if (rdata->msg_info.cseq->cseq != dd->uac_state->cseq) { 413 dd->uac_state->cseq = rdata->msg_info.cseq->cseq; 414 dd->uac_state->rseq = rseq - 1; 415 } 416 417 /* Ignore provisional response retransmission */ 418 if (rseq <= dd->uac_state->rseq) { 419 /* This should have been handled before */ 420 return; 421 422 /* Ignore provisional response with out-of-order RSeq */ 423 } else if (rseq != dd->uac_state->rseq + 1) { 424 PJ_LOG(4,(dd->inv->dlg->obj_name, 425 "Ignoring provisional response because RSeq jump " 426 "(expecting %u, got %u)", 427 dd->uac_state->rseq+1, rseq)); 428 return; 429 } 430 431 /* Update our RSeq */ 432 dd->uac_state->rseq = rseq; 433 434 /* Create PRACK */ 435 status = pjsip_dlg_create_request(dd->inv->dlg, &pjsip_prack_method, 436 -1, &tdata); 437 if (status != PJ_SUCCESS) { 438 PJ_LOG(4,(dd->inv->dlg->obj_name, 439 "Error creating PRACK request (status=%d)", status)); 440 return; 441 } 442 443 /* Create RAck header */ 444 rack.ptr = rack_buf; 445 rack.slen = pj_ansi_snprintf(rack.ptr, sizeof(rack_buf), 446 "%u %u %.*s", 447 rseq, rdata->msg_info.cseq->cseq, 448 (int)tsx->method.name.slen, 449 tsx->method.name.ptr); 450 PJ_ASSERT_ON_FAIL(rack.slen > 0 && rack.slen < (int)sizeof(rack_buf), 451 { pjsip_tx_data_dec_ref(tdata); return; }); 452 rack_hdr = pjsip_generic_string_hdr_create(tdata->pool, &RACK, &rack); 453 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*) rack_hdr); 454 455 /* Send PRACK */ 456 pjsip_dlg_send_request(dd->inv->dlg, tdata, 457 mod_100rel.mod.id, (void*) dd); 458 459 } 460 461 462 /* 463 * API: init module 464 */ 465 PJ_DEF(pj_status_t) pjsip_100rel_init_module(pjsip_endpoint *endpt) 466 { 467 return pjsip_endpt_register_module(endpt, &mod_100rel.mod); 468 } 469 470 471 /* 472 * API: attach 100rel support in invite session. Called by 473 * sip_inv.c 474 */ 475 PJ_DEF(pj_status_t) pjsip_100rel_attach(pjsip_inv_session *inv) 476 { 477 dlg_data *dd; 478 479 /* Check that 100rel module has been initialized */ 480 PJ_ASSERT_RETURN(mod_100rel.mod.id >= 0, PJ_EINVALIDOP); 481 482 /* Create and attach as dialog usage */ 483 dd = PJ_POOL_ZALLOC_T(inv->dlg->pool, dlg_data); 484 dd->inv = inv; 485 pjsip_dlg_add_usage(inv->dlg, &mod_100rel.mod, (void*)dd); 486 487 PJ_LOG(5,(dd->inv->dlg->obj_name, "100rel module attached")); 488 489 return PJ_SUCCESS; 486 487 } else { 488 /* No it doesn't match */ 489 PJ_LOG(4,(dd->inv->dlg->obj_name, 490 "Rx PRACK with no matching reliable response")); 491 return PJ_EIGNORED; 492 } 493 494 return PJ_SUCCESS; 490 495 } 491 496 … … 498 503 struct pj_timer_entry *entry) 499 504 { 500 dlg_data *dd; 501 tx_data_list_t *tl; 502 pjsip_tx_data *tdata; 503 pj_bool_t final; 504 pj_time_val delay; 505 506 PJ_UNUSED_ARG(timer_heap); 507 508 dd = (dlg_data*) entry->user_data; 509 510 entry->id = PJ_FALSE; 511 512 ++dd->uas_state->retransmit_count; 513 if (dd->uas_state->retransmit_count >= 7) { 514 /* If a reliable provisional response is retransmitted for 515 64*T1 seconds without reception of a corresponding PRACK, 516 the UAS SHOULD reject the original request with a 5xx 517 response. 518 */ 519 pj_str_t reason = pj_str("Reliable response timed out"); 520 pj_status_t status; 521 522 /* Clear all pending responses */ 523 clear_all_responses(dd); 524 525 /* Send 500 response */ 526 status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata); 527 if (status == PJ_SUCCESS) { 528 pjsip_dlg_send_response(dd->inv->dlg, 529 dd->inv->invite_tsx, 530 tdata); 531 } 532 return; 505 dlg_data *dd; 506 tx_data_list_t *tl; 507 pjsip_tx_data *tdata; 508 pj_bool_t final; 509 pj_time_val delay; 510 511 PJ_UNUSED_ARG(timer_heap); 512 513 dd = (dlg_data*) entry->user_data; 514 515 entry->id = PJ_FALSE; 516 517 ++dd->uas_state->retransmit_count; 518 if (dd->uas_state->retransmit_count >= 7) { 519 /* If a reliable provisional response is retransmitted for 520 64*T1 seconds without reception of a corresponding PRACK, 521 the UAS SHOULD reject the original request with a 5xx 522 response. 523 */ 524 pj_str_t reason = pj_str("Reliable response timed out"); 525 pj_status_t status; 526 527 /* Clear all pending responses */ 528 clear_all_responses(dd); 529 530 /* Send 500 response */ 531 status = pjsip_inv_end_session(dd->inv, 500, &reason, &tdata); 532 if (status == PJ_SUCCESS) { 533 pjsip_dlg_send_response(dd->inv->dlg, 534 dd->inv->invite_tsx, 535 tdata); 533 536 } 534 535 pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list)); 536 tl = dd->uas_state->tx_data_list.next; 537 tdata = tl->tdata; 538 539 pjsip_tx_data_add_ref(tdata); 540 final = tdata->msg->line.status.code >= 200; 541 542 if (dd->uas_state->retransmit_count == 1) { 543 pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata); 544 } else { 545 pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata); 546 } 547 548 if (final) { 549 /* This is final response, which will be retransmitted by 550 * UA layer. There's no more task to do, so clear the 551 * transmission list and bail out. 552 */ 553 clear_all_responses(dd); 554 return; 555 } 556 557 /* Schedule next retransmission */ 558 if (dd->uas_state->retransmit_count < 6) { 559 delay.sec = 0; 560 delay.msec = (1 << dd->uas_state->retransmit_count) * 561 PJSIP_T1_TIMEOUT; 562 pj_time_val_normalize(&delay); 563 } else { 564 delay.sec = 1; 565 delay.msec = 500; 566 } 567 568 569 pjsip_endpt_schedule_timer(dd->inv->dlg->endpt, 570 &dd->uas_state->retransmit_timer, 571 &delay); 572 573 entry->id = PJ_TRUE; 574 } 537 return; 538 } 539 540 pj_assert(!pj_list_empty(&dd->uas_state->tx_data_list)); 541 tl = dd->uas_state->tx_data_list.next; 542 tdata = tl->tdata; 543 544 pjsip_tx_data_add_ref(tdata); 545 final = tdata->msg->line.status.code >= 200; 546 547 if (dd->uas_state->retransmit_count == 1) { 548 pjsip_tsx_send_msg(dd->inv->invite_tsx, tdata); 549 } else { 550 pjsip_tsx_retransmit_no_state(dd->inv->invite_tsx, tdata); 551 } 552 553 if (final) { 554 /* This is final response, which will be retransmitted by 555 * UA layer. There's no more task to do, so clear the 556 * transmission list and bail out. 557 */ 558 clear_all_responses(dd); 559 return; 560 } 561 562 /* Schedule next retransmission */ 563 if (dd->uas_state->retransmit_count < 6) { 564 delay.sec = 0; 565 delay.msec = (1 << dd->uas_state->retransmit_count) * 566 PJSIP_T1_TIMEOUT; 567 pj_time_val_normalize(&delay); 568 } else { 569 delay.sec = 1; 570 delay.msec = 500; 571 } 572 573 574 pjsip_endpt_schedule_timer(dd->inv->dlg->endpt, 575 &dd->uas_state->retransmit_timer, 576 &delay); 577 578 entry->id = PJ_TRUE; 579 } 580 575 581 576 582 /* Clone response. */ … … 578 584 const pjsip_tx_data *src) 579 585 { 580 pjsip_tx_data *dst; 581 const pjsip_hdr *hsrc; 582 pjsip_msg *msg; 583 pj_status_t status; 584 585 status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst); 586 if (status != PJ_SUCCESS) 587 return NULL; 588 589 msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG); 590 dst->msg = msg; 591 pjsip_tx_data_add_ref(dst); 592 593 /* Duplicate status line */ 594 msg->line.status.code = src->msg->line.status.code; 595 pj_strdup(dst->pool, &msg->line.status.reason, 596 &src->msg->line.status.reason); 597 598 /* Duplicate all headers */ 599 hsrc = src->msg->hdr.next; 600 while (hsrc != &src->msg->hdr) { 601 pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc); 602 pjsip_msg_add_hdr(msg, h); 603 hsrc = hsrc->next; 604 } 605 606 /* Duplicate message body */ 607 if (src->msg->body) 608 msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body); 609 610 PJ_LOG(5,(dd->inv->dlg->obj_name, 611 "Reliable response %s created", 612 pjsip_tx_data_get_info(dst))); 613 614 return dst; 615 } 616 617 /* Check if pending response has SDP */ 586 pjsip_tx_data *dst; 587 const pjsip_hdr *hsrc; 588 pjsip_msg *msg; 589 pj_status_t status; 590 591 status = pjsip_endpt_create_tdata(dd->inv->dlg->endpt, &dst); 592 if (status != PJ_SUCCESS) 593 return NULL; 594 595 msg = pjsip_msg_create(dst->pool, PJSIP_RESPONSE_MSG); 596 dst->msg = msg; 597 pjsip_tx_data_add_ref(dst); 598 599 /* Duplicate status line */ 600 msg->line.status.code = src->msg->line.status.code; 601 pj_strdup(dst->pool, &msg->line.status.reason, 602 &src->msg->line.status.reason); 603 604 /* Duplicate all headers */ 605 hsrc = src->msg->hdr.next; 606 while (hsrc != &src->msg->hdr) { 607 pjsip_hdr *h = (pjsip_hdr*) pjsip_hdr_clone(dst->pool, hsrc); 608 pjsip_msg_add_hdr(msg, h); 609 hsrc = hsrc->next; 610 } 611 612 /* Duplicate message body */ 613 if (src->msg->body) 614 msg->body = pjsip_msg_body_clone(dst->pool, src->msg->body); 615 616 PJ_LOG(5,(dd->inv->dlg->obj_name, 617 "Reliable response %s created", 618 pjsip_tx_data_get_info(dst))); 619 620 return dst; 621 } 622 623 624 /* Check if any pending response in transmission list has SDP */ 618 625 static pj_bool_t has_sdp(dlg_data *dd) 619 626 { 620 621 622 623 624 625 626 627 628 629 627 tx_data_list_t *tl; 628 629 tl = dd->uas_state->tx_data_list.next; 630 while (tl != &dd->uas_state->tx_data_list) { 631 if (tl->tdata->msg->body) 632 return PJ_TRUE; 633 tl = tl->next; 634 } 635 636 return PJ_FALSE; 630 637 } 631 638 … … 635 642 pjsip_tx_data *tdata) 636 643 { 637 pjsip_cseq_hdr *cseq_hdr; 638 pjsip_generic_string_hdr *rseq_hdr; 639 pjsip_require_hdr *req_hdr; 640 int status_code; 641 dlg_data *dd; 642 pjsip_tx_data *old_tdata; 643 pj_status_t status; 644 645 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, 644 pjsip_cseq_hdr *cseq_hdr; 645 pjsip_generic_string_hdr *rseq_hdr; 646 pjsip_require_hdr *req_hdr; 647 int status_code; 648 dlg_data *dd; 649 pjsip_tx_data *old_tdata; 650 pj_status_t status; 651 652 PJ_ASSERT_RETURN(tdata->msg->type == PJSIP_RESPONSE_MSG, 653 PJSIP_ENOTRESPONSEMSG); 654 655 status_code = tdata->msg->line.status.code; 656 657 /* 100 response doesn't need PRACK */ 658 if (status_code == 100) 659 return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); 660 661 662 /* Get the 100rel data attached to this dialog */ 663 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 664 PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP); 665 666 667 /* Clone tdata. 668 * We need to clone tdata because we may need to keep it in our 669 * retransmission list, while the original dialog may modify it 670 * if it wants to send another response. 671 */ 672 old_tdata = tdata; 673 tdata = clone_tdata(dd, old_tdata); 674 pjsip_tx_data_dec_ref(old_tdata); 675 676 677 /* Get CSeq header, and make sure this is INVITE response */ 678 cseq_hdr = (pjsip_cseq_hdr*) 679 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); 680 PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG); 681 PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD, 682 PJ_EINVALIDOP); 683 684 /* Remove existing Require header */ 685 req_hdr = find_req_hdr(tdata->msg); 686 if (req_hdr) { 687 pj_list_erase(req_hdr); 688 } 689 690 /* Remove existing RSeq header */ 691 rseq_hdr = (pjsip_generic_string_hdr*) 692 pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL); 693 if (rseq_hdr) 694 pj_list_erase(rseq_hdr); 695 696 /* Different treatment for provisional and final response */ 697 if (status_code/100 == 2) { 698 699 /* RFC 3262 Section 3: UAS Behavior: 700 701 The UAS MAY send a final response to the initial request 702 before having received PRACKs for all unacknowledged 703 reliable provisional responses, unless the final response 704 is 2xx and any of the unacknowledged reliable provisional 705 responses contained a session description. In that case, 706 it MUST NOT send a final response until those provisional 707 responses are acknowledged. 708 */ 709 710 if (dd->uas_state && has_sdp(dd)) { 711 /* Yes we have transmitted 1xx with SDP reliably. 712 * In this case, must queue the 2xx response. 713 */ 714 tx_data_list_t *tl; 715 716 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); 717 tl->tdata = tdata; 718 tl->rseq = (pj_uint32_t)-1; 719 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 720 721 /* Will send later */ 722 status = PJ_SUCCESS; 723 724 PJ_LOG(4,(dd->inv->dlg->obj_name, 725 "2xx response will be sent after PRACK")); 726 727 } else if (dd->uas_state) { 728 /* 729 RFC 3262 Section 3: UAS Behavior: 730 731 If the UAS does send a final response when reliable 732 responses are still unacknowledged, it SHOULD NOT 733 continue to retransmit the unacknowledged reliable 734 provisional responses, but it MUST be prepared to 735 process PRACK requests for those outstanding 736 responses. 737 */ 738 739 PJ_LOG(4,(dd->inv->dlg->obj_name, 740 "No SDP sent so far, sending 2xx now")); 741 742 /* Cancel the retransmit timer */ 743 if (dd->uas_state->retransmit_timer.id) { 744 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 745 &dd->uas_state->retransmit_timer); 746 dd->uas_state->retransmit_timer.id = PJ_FALSE; 747 } 748 749 /* Clear all pending responses (drop 'em) */ 750 clear_all_responses(dd); 751 752 /* And transmit the 2xx response */ 753 status=pjsip_dlg_send_response(inv->dlg, 754 inv->invite_tsx, tdata); 755 756 } else { 757 /* We didn't send any reliable provisional response */ 758 759 /* Transmit the 2xx response */ 760 status=pjsip_dlg_send_response(inv->dlg, 761 inv->invite_tsx, tdata); 762 } 763 764 } else if (status_code >= 300) { 765 766 /* 767 RFC 3262 Section 3: UAS Behavior: 768 769 If the UAS does send a final response when reliable 770 responses are still unacknowledged, it SHOULD NOT 771 continue to retransmit the unacknowledged reliable 772 provisional responses, but it MUST be prepared to 773 process PRACK requests for those outstanding 774 responses. 775 */ 776 777 /* Cancel the retransmit timer */ 778 if (dd->uas_state && dd->uas_state->retransmit_timer.id) { 779 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 780 &dd->uas_state->retransmit_timer); 781 dd->uas_state->retransmit_timer.id = PJ_FALSE; 782 783 /* Clear all pending responses (drop 'em) */ 784 clear_all_responses(dd); 785 } 786 787 /* And transmit the 2xx response */ 788 status=pjsip_dlg_send_response(inv->dlg, 789 inv->invite_tsx, tdata); 790 791 } else { 792 /* 793 * This is provisional response. 794 */ 795 char rseq_str[32]; 796 pj_str_t rseq; 797 tx_data_list_t *tl; 798 799 /* Create UAS state if we don't have one */ 800 if (dd->uas_state == NULL) { 801 dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool, 802 uas_state_t); 803 dd->uas_state->cseq = cseq_hdr->cseq; 804 dd->uas_state->rseq = pj_rand() % 0x7FFF; 805 pj_list_init(&dd->uas_state->tx_data_list); 806 dd->uas_state->retransmit_timer.user_data = dd; 807 dd->uas_state->retransmit_timer.cb = &on_retransmit; 808 } 809 810 /* Check that CSeq match */ 811 PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq, 646 812 PJ_EINVALIDOP); 647 648 status_code = tdata->msg->line.status.code; 649 650 /* 100 response doesn't need PRACK */ 651 if (status_code == 100) 652 return pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); 653 654 /* Get the dialog data */ 655 dd = (dlg_data*) inv->dlg->mod_data[mod_100rel.mod.id]; 656 PJ_ASSERT_RETURN(dd != NULL, PJ_EINVALIDOP); 657 658 659 /* Clone tdata */ 660 old_tdata = tdata; 661 tdata = clone_tdata(dd, old_tdata); 662 pjsip_tx_data_dec_ref(old_tdata); 663 664 /* Get CSeq header */ 665 cseq_hdr = (pjsip_cseq_hdr*) 666 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_CSEQ, NULL); 667 PJ_ASSERT_RETURN(cseq_hdr != NULL, PJ_EBUG); 668 PJ_ASSERT_RETURN(cseq_hdr->method.id == PJSIP_INVITE_METHOD, 669 PJ_EINVALIDOP); 670 671 /* Remove existing Require header */ 672 req_hdr = find_req_hdr(tdata->msg); 673 if (req_hdr) { 674 pj_list_erase(req_hdr); 813 814 /* Add Require header */ 815 req_hdr = pjsip_require_hdr_create(tdata->pool); 816 req_hdr->count = 1; 817 req_hdr->values[0] = tag_100rel; 818 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr); 819 820 /* Add RSeq header */ 821 pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u", 822 dd->uas_state->rseq); 823 rseq = pj_str(rseq_str); 824 rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool, 825 &RSEQ, &rseq); 826 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr); 827 828 /* Create list entry for this response */ 829 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); 830 tl->tdata = tdata; 831 tl->rseq = dd->uas_state->rseq++; 832 833 /* Add to queue if there's pending response, otherwise 834 * transmit immediately. 835 */ 836 if (!pj_list_empty(&dd->uas_state->tx_data_list)) { 837 838 int code = tdata->msg->line.status.code; 839 840 /* Will send later */ 841 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 842 status = PJ_SUCCESS; 843 844 PJ_LOG(4,(dd->inv->dlg->obj_name, 845 "Reliable %d response enqueued (%d pending)", 846 code, pj_list_size(&dd->uas_state->tx_data_list))); 847 848 } else { 849 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 850 851 dd->uas_state->retransmit_count = 0; 852 on_retransmit(NULL, &dd->uas_state->retransmit_timer); 853 status = PJ_SUCCESS; 675 854 } 676 677 /* Remove existing RSeq header */ 678 rseq_hdr = (pjsip_generic_string_hdr*) 679 pjsip_msg_find_hdr_by_name(tdata->msg, &RSEQ, NULL); 680 if (rseq_hdr) 681 pj_list_erase(rseq_hdr); 682 683 /* Different treatment for provisional and final response */ 684 if (status_code/100 == 2) { 685 686 /* RFC 3262 Section 3: UAS Behavior: 687 688 The UAS MAY send a final response to the initial request 689 before having received PRACKs for all unacknowledged 690 reliable provisional responses, unless the final response 691 is 2xx and any of the unacknowledged reliable provisional 692 responses contained a session description. In that case, 693 it MUST NOT send a final response until those provisional 694 responses are acknowledged. 695 */ 696 697 if (dd->uas_state && has_sdp(dd)) { 698 /* Yes we have transmitted 1xx with SDP reliably. 699 * In this case, must queue the 2xx response. 700 */ 701 tx_data_list_t *tl; 702 703 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); 704 tl->tdata = tdata; 705 tl->rseq = (pj_uint32_t)-1; 706 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 707 708 /* Will send later */ 709 status = PJ_SUCCESS; 710 711 PJ_LOG(4,(dd->inv->dlg->obj_name, 712 "2xx response will be sent after PRACK")); 713 714 } else if (dd->uas_state) { 715 /* 716 If the UAS does send a final response when reliable 717 responses are still unacknowledged, it SHOULD NOT 718 continue to retransmit the unacknowledged reliable 719 provisional responses, but it MUST be prepared to 720 process PRACK requests for those outstanding 721 responses. 722 */ 723 724 PJ_LOG(4,(dd->inv->dlg->obj_name, 725 "No SDP sent so far, sending 2xx now")); 726 727 /* Cancel the retransmit timer */ 728 if (dd->uas_state->retransmit_timer.id) { 729 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 730 &dd->uas_state->retransmit_timer); 731 dd->uas_state->retransmit_timer.id = PJ_FALSE; 732 } 733 734 /* Clear all pending responses (drop 'em) */ 735 clear_all_responses(dd); 736 737 /* And transmit the 2xx response */ 738 status=pjsip_dlg_send_response(inv->dlg, 739 inv->invite_tsx, tdata); 740 741 } else { 742 /* We didn't send any reliable provisional response */ 743 744 /* Transmit the 2xx response */ 745 status=pjsip_dlg_send_response(inv->dlg, 746 inv->invite_tsx, tdata); 747 } 748 749 } else if (status_code >= 300) { 750 751 /* 752 If the UAS does send a final response when reliable 753 responses are still unacknowledged, it SHOULD NOT 754 continue to retransmit the unacknowledged reliable 755 provisional responses, but it MUST be prepared to 756 process PRACK requests for those outstanding 757 responses. 758 */ 759 760 /* Cancel the retransmit timer */ 761 if (dd->uas_state && dd->uas_state->retransmit_timer.id) { 762 pjsip_endpt_cancel_timer(dd->inv->dlg->endpt, 763 &dd->uas_state->retransmit_timer); 764 dd->uas_state->retransmit_timer.id = PJ_FALSE; 765 766 /* Clear all pending responses (drop 'em) */ 767 clear_all_responses(dd); 768 } 769 770 /* And transmit the 2xx response */ 771 status=pjsip_dlg_send_response(inv->dlg, 772 inv->invite_tsx, tdata); 773 774 } else { 775 /* 776 * This is provisional response. 777 */ 778 char rseq_str[32]; 779 pj_str_t rseq; 780 tx_data_list_t *tl; 781 782 /* Create UAS state if we don't have one */ 783 if (dd->uas_state == NULL) { 784 dd->uas_state = PJ_POOL_ZALLOC_T(inv->dlg->pool, 785 uas_state_t); 786 dd->uas_state->cseq = cseq_hdr->cseq; 787 dd->uas_state->rseq = pj_rand() % 0x7FFF; 788 pj_list_init(&dd->uas_state->tx_data_list); 789 dd->uas_state->retransmit_timer.user_data = dd; 790 dd->uas_state->retransmit_timer.cb = &on_retransmit; 791 } 792 793 /* Check that CSeq match */ 794 PJ_ASSERT_RETURN(cseq_hdr->cseq == dd->uas_state->cseq, 795 PJ_EINVALIDOP); 796 797 /* Add Require header */ 798 req_hdr = pjsip_require_hdr_create(tdata->pool); 799 req_hdr->count = 1; 800 req_hdr->values[0] = tag_100rel; 801 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)req_hdr); 802 803 /* Add RSeq header */ 804 pj_ansi_snprintf(rseq_str, sizeof(rseq_str), "%u", 805 dd->uas_state->rseq); 806 rseq = pj_str(rseq_str); 807 rseq_hdr = pjsip_generic_string_hdr_create(tdata->pool, 808 &RSEQ, &rseq); 809 pjsip_msg_add_hdr(tdata->msg, (pjsip_hdr*)rseq_hdr); 810 811 /* Create list entry for this response */ 812 tl = PJ_POOL_ZALLOC_T(tdata->pool, tx_data_list_t); 813 tl->tdata = tdata; 814 tl->rseq = dd->uas_state->rseq++; 815 816 /* Add to queue if there's pending response, otherwise 817 * transmit immediately. 818 */ 819 if (!pj_list_empty(&dd->uas_state->tx_data_list)) { 820 821 int code = tdata->msg->line.status.code; 822 823 /* Will send later */ 824 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 825 status = PJ_SUCCESS; 826 827 PJ_LOG(4,(dd->inv->dlg->obj_name, 828 "Reliable %d response enqueued (%d pending)", 829 code, pj_list_size(&dd->uas_state->tx_data_list))); 830 831 } else { 832 pj_list_push_back(&dd->uas_state->tx_data_list, tl); 833 834 dd->uas_state->retransmit_count = 0; 835 on_retransmit(NULL, &dd->uas_state->retransmit_timer); 836 status = PJ_SUCCESS; 837 } 838 839 } 840 841 return status; 842 } 843 844 845 #endif /* PJSIP_HAS_100REL */ 855 856 } 857 858 return status; 859 } 860 861 -
pjproject/trunk/pjsip/src/pjsip-ua/sip_inv.c
r1463 r1469 32 32 #include <pj/log.h> 33 33 34 /* 35 * Note on offer/answer: 36 * 37 * The offer/answer framework in this implementation assumes the occurence 38 * of SDP in a particular request/response according to this table: 39 40 offer answer Note: 41 ======================================================================== 42 INVITE X INVITE may contain offer 43 18x/INVITE X X Response may contain offer or answer 44 2xx/INVITE X X Response may contain offer or answer 45 ACK X ACK may contain answer 46 47 PRACK X PRACK can only contain answer 48 2xx/PRACK Response may not have offer nor answer 49 50 UPDATE X UPDATE may only contain offer 51 2xx/UPDATE X Response may only contain answer 52 ======================================================================== 53 54 * 55 */ 34 56 35 57 #define THIS_FILE "sip_inv.c" … … 47 69 }; 48 70 71 /* UPDATE method */ 72 const pjsip_method pjsip_update_method = 73 { 74 PJSIP_OTHER_METHOD, 75 { "UPDATE", 6 } 76 }; 77 49 78 /* 50 79 * Static prototypes. … … 67 96 pjsip_transaction *tsx, 68 97 pjsip_rx_data *rdata); 98 static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv ); 99 static pjsip_msg_body *create_sdp_body(pj_pool_t *pool, 100 const pjmedia_sdp_session *c_sdp); 69 101 static pj_status_t process_answer( pjsip_inv_session *inv, 70 102 int st_code, … … 120 152 static pj_status_t mod_inv_load(pjsip_endpoint *endpt) 121 153 { 122 pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}}; 154 pj_str_t allowed[] = {{"INVITE", 6}, {"ACK",3}, {"BYE",3}, {"CANCEL",6}, 155 { "UPDATE", 6}}; 123 156 pj_str_t accepted = { "application/sdp", 15 }; 124 157 125 /* Register supported methods: INVITE, ACK, BYE, CANCEL */158 /* Register supported methods: INVITE, ACK, BYE, CANCEL, UPDATE */ 126 159 pjsip_endpt_add_capability(endpt, &mod_inv.mod, PJSIP_H_ALLOW, NULL, 127 160 PJ_ARRAY_SIZE(allowed), allowed); … … 187 220 prev_state != PJSIP_INV_STATE_DISCONNECTED) 188 221 { 222 if (inv->last_ack) { 223 pjsip_tx_data_dec_ref(inv->last_ack); 224 inv->last_ack = NULL; 225 } 226 pjsip_100rel_end_session(inv); 189 227 pjsip_dlg_dec_session(inv->dlg, &mod_inv.mod); 190 228 } … … 210 248 211 249 250 /* 251 * Check if outgoing request needs to have SDP answer. 252 * This applies for both ACK and PRACK requests. 253 */ 254 static pjmedia_sdp_session *inv_has_pending_answer(pjsip_inv_session *inv, 255 pjsip_transaction *tsx) 256 { 257 pjmedia_sdp_neg_state neg_state; 258 pjmedia_sdp_session *sdp = NULL; 259 pj_status_t status; 260 261 /* If SDP negotiator is ready, start negotiation. */ 262 263 /* Start nego when appropriate. */ 264 neg_state = inv->neg ? pjmedia_sdp_neg_get_state(inv->neg) : 265 PJMEDIA_SDP_NEG_STATE_NULL; 266 267 if (neg_state == PJMEDIA_SDP_NEG_STATE_DONE) { 268 269 /* Nothing to do */ 270 271 } else if (neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO && 272 pjmedia_sdp_neg_has_local_answer(inv->neg) ) 273 { 274 struct tsx_inv_data *tsx_inv_data; 275 276 /* Get invite session's transaction data */ 277 tsx_inv_data = (struct tsx_inv_data*) tsx->mod_data[mod_inv.mod.id]; 278 279 status = inv_negotiate_sdp(inv); 280 if (status != PJ_SUCCESS) 281 return NULL; 282 283 /* Mark this transaction has having SDP offer/answer done. */ 284 tsx_inv_data->sdp_done = 1; 285 286 status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp); 287 288 } else { 289 /* This remark is only valid for ACK. 290 PJ_LOG(4,(inv->dlg->obj_name, 291 "FYI, the SDP negotiator state (%s) is in a mess " 292 "when sending this ACK/PRACK request", 293 pjmedia_sdp_neg_state_str(neg_state))); 294 */ 295 } 296 297 return sdp; 298 } 299 212 300 213 301 /* … … 216 304 static pj_status_t inv_send_ack(pjsip_inv_session *inv, pjsip_rx_data *rdata) 217 305 { 218 pjsip_tx_data *tdata;219 306 pj_status_t status; 220 307 … … 222 309 pjsip_rx_data_get_info(rdata))); 223 310 224 status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 225 rdata->msg_info.cseq->cseq, &tdata); 226 if (status != PJ_SUCCESS) { 227 /* Better luck next time */ 228 pj_assert(!"Unable to create ACK!"); 229 return status; 230 } 231 232 status = pjsip_dlg_send_request(inv->dlg, tdata, -1, NULL); 311 /* Check if we have cached ACK request */ 312 if (inv->last_ack && rdata->msg_info.cseq->cseq == inv->last_ack_cseq) { 313 pjsip_tx_data_add_ref(inv->last_ack); 314 } else { 315 pjmedia_sdp_session *sdp = NULL; 316 317 /* Destroy last_ack */ 318 if (inv->last_ack) { 319 pjsip_tx_data_dec_ref(inv->last_ack); 320 inv->last_ack = NULL; 321 } 322 323 /* Create new ACK request */ 324 status = pjsip_dlg_create_request(inv->dlg, pjsip_get_ack_method(), 325 rdata->msg_info.cseq->cseq, 326 &inv->last_ack); 327 if (status != PJ_SUCCESS) { 328 /* Better luck next time */ 329 pj_assert(!"Unable to create ACK!"); 330 return status; 331 } 332 333 /* See if we have pending SDP answer to send */ 334 sdp = inv_has_pending_answer(inv, inv->invite_tsx); 335 if (sdp) { 336 inv->last_ack->msg->body=create_sdp_body(inv->last_ack->pool, sdp); 337 } 338 339 340 /* Keep this for subsequent response retransmission */ 341 inv->last_ack_cseq = rdata->msg_info.cseq->cseq; 342 pjsip_tx_data_add_ref(inv->last_ack); 343 } 344 345 /* Send ACK */ 346 status = pjsip_dlg_send_request(inv->dlg, inv->last_ack, -1, NULL); 233 347 if (status != PJ_SUCCESS) { 234 348 /* Better luck next time */ … … 493 607 options |= PJSIP_INV_SUPPORT_100REL; 494 608 495 #if !PJSIP_HAS_100REL496 /* options cannot specify 100rel if 100rel is disabled */497 PJ_ASSERT_RETURN(498 (options & (PJSIP_INV_REQUIRE_100REL | PJSIP_INV_SUPPORT_100REL))==0,499 PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION));500 501 #endif502 503 609 if (options & PJSIP_INV_REQUIRE_TIMER) 504 610 options |= PJSIP_INV_SUPPORT_TIMER; … … 539 645 pjsip_dlg_inc_session(dlg, &mod_inv.mod); 540 646 541 #if PJSIP_HAS_100REL542 647 /* Create 100rel handler */ 543 648 pjsip_100rel_attach(inv); 544 #endif545 649 546 650 /* Done */ … … 944 1048 options |= PJSIP_INV_SUPPORT_100REL; 945 1049 946 #if !PJSIP_HAS_100REL947 /* options cannot specify 100rel if 100rel is disabled */948 PJ_ASSERT_RETURN(949 (options & (PJSIP_INV_REQUIRE_100REL | PJSIP_INV_SUPPORT_100REL))==0,950 PJSIP_ERRNO_FROM_SIP_STATUS(PJSIP_SC_BAD_EXTENSION));951 952 #endif953 954 1050 if (options & PJSIP_INV_REQUIRE_TIMER) 955 1051 options |= PJSIP_INV_SUPPORT_TIMER; … … 1021 1117 inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; 1022 1118 1023 #if PJSIP_HAS_100REL1024 1119 /* Create 100rel handler */ 1025 1120 if (inv->options & PJSIP_INV_REQUIRE_100REL) { 1026 1121 pjsip_100rel_attach(inv); 1027 1122 } 1028 #endif1029 1123 1030 1124 /* Done */ … … 1240 1334 1241 1335 /* 1242 * Negotiate SDP.1336 * Initiate SDP negotiation in the SDP negotiator. 1243 1337 */ 1244 1338 static pj_status_t inv_negotiate_sdp( pjsip_inv_session *inv ) … … 1782 1876 PJ_DEF(pj_status_t) pjsip_inv_update ( pjsip_inv_session *inv, 1783 1877 const pj_str_t *new_contact, 1784 const pjmedia_sdp_session * new_offer,1878 const pjmedia_sdp_session *offer, 1785 1879 pjsip_tx_data **p_tdata ) 1786 1880 { 1787 PJ_UNUSED_ARG(inv); 1788 PJ_UNUSED_ARG(new_contact); 1789 PJ_UNUSED_ARG(new_offer); 1790 PJ_UNUSED_ARG(p_tdata); 1791 1792 PJ_TODO(CREATE_UPDATE_REQUEST); 1793 return PJ_ENOTSUP; 1881 pjsip_contact_hdr *contact_hdr = NULL; 1882 pjsip_tx_data *tdata = NULL; 1883 pjmedia_sdp_session *sdp_copy; 1884 pj_status_t status = PJ_SUCCESS; 1885 1886 /* Verify arguments. */ 1887 PJ_ASSERT_RETURN(inv && p_tdata && offer, PJ_EINVAL); 1888 1889 /* Dialog must have been established */ 1890 PJ_ASSERT_RETURN(inv->dlg->state == PJSIP_DIALOG_STATE_ESTABLISHED, 1891 PJ_EINVALIDOP); 1892 1893 /* Invite session must not have been disconnected */ 1894 PJ_ASSERT_RETURN(inv->state < PJSIP_INV_STATE_DISCONNECTED, 1895 PJ_EINVALIDOP); 1896 1897 /* Lock dialog. */ 1898 pjsip_dlg_inc_lock(inv->dlg); 1899 1900 /* Process offer */ 1901 if (pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE) { 1902 PJ_LOG(4,(inv->dlg->obj_name, 1903 "Invalid SDP offer/answer state for UPDATE")); 1904 status = PJ_EINVALIDOP; 1905 goto on_error; 1906 } 1907 1908 status = pjmedia_sdp_neg_modify_local_offer(inv->pool,inv->neg, 1909 offer); 1910 if (status != PJ_SUCCESS) 1911 goto on_error; 1912 1913 1914 /* Update Contact if required */ 1915 if (new_contact) { 1916 pj_str_t tmp; 1917 const pj_str_t STR_CONTACT = { "Contact", 7 }; 1918 1919 pj_strdup_with_null(inv->dlg->pool, &tmp, new_contact); 1920 contact_hdr = (pjsip_contact_hdr*) 1921 pjsip_parse_hdr(inv->dlg->pool, &STR_CONTACT, 1922 tmp.ptr, tmp.slen, NULL); 1923 if (!contact_hdr) { 1924 status = PJSIP_EINVALIDURI; 1925 goto on_error; 1926 } 1927 1928 inv->dlg->local.contact = contact_hdr; 1929 } 1930 1931 /* Create request */ 1932 status = pjsip_dlg_create_request(inv->dlg, &pjsip_update_method, 1933 -1, &tdata); 1934 if (status != PJ_SUCCESS) 1935 goto on_error; 1936 1937 /* Attach SDP body */ 1938 sdp_copy = pjmedia_sdp_session_clone(tdata->pool, offer); 1939 pjsip_create_sdp_body(tdata->pool, sdp_copy, &tdata->msg->body); 1940 1941 /* Unlock dialog. */ 1942 pjsip_dlg_dec_lock(inv->dlg); 1943 1944 *p_tdata = tdata; 1945 1946 return PJ_SUCCESS; 1947 1948 on_error: 1949 if (tdata) 1950 pjsip_tx_data_dec_ref(tdata); 1951 1952 /* Unlock dialog. */ 1953 pjsip_dlg_dec_lock(inv->dlg); 1954 1955 return status; 1794 1956 } 1795 1957 … … 1833 1995 PJ_EINVALIDOP); 1834 1996 1835 #if PJSIP_HAS_100REL1836 1997 if (inv->options & PJSIP_INV_REQUIRE_100REL) { 1837 1998 status = pjsip_100rel_tx_response(inv, tdata); 1838 1999 } else 1839 #endif1840 2000 { 1841 2001 status = pjsip_dlg_send_response(inv->dlg, inv->invite_tsx, tdata); 1842 2002 } 1843 2003 … … 1988 2148 1989 2149 /* 2150 * Respond to incoming UPDATE request. 2151 */ 2152 static void inv_respond_incoming_update(pjsip_inv_session *inv, 2153 pjsip_rx_data *rdata) 2154 { 2155 pjmedia_sdp_neg_state neg_state; 2156 pj_status_t status; 2157 pjsip_tx_data *tdata = NULL; 2158 2159 neg_state = pjmedia_sdp_neg_get_state(inv->neg); 2160 2161 /* Send 491 if we receive UPDATE while we're waiting for an answer */ 2162 if (neg_state == PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER) { 2163 status = pjsip_dlg_create_response(inv->dlg, rdata, 2164 PJSIP_SC_REQUEST_PENDING, NULL, 2165 &tdata); 2166 } 2167 /* Send 500 with Retry-After header set randomly between 0 and 10 if we 2168 * receive UPDATE while we haven't sent answer. 2169 */ 2170 else if (neg_state == PJMEDIA_SDP_NEG_STATE_REMOTE_OFFER || 2171 neg_state == PJMEDIA_SDP_NEG_STATE_WAIT_NEGO) { 2172 status = pjsip_dlg_create_response(inv->dlg, rdata, 2173 PJSIP_SC_INTERNAL_SERVER_ERROR, 2174 NULL, &tdata); 2175 2176 } else { 2177 /* We receive new offer from remote */ 2178 inv_check_sdp_in_incoming_msg(inv, pjsip_rdata_get_tsx(rdata), rdata); 2179 2180 /* Application MUST have supplied the answer by now. 2181 * If so, negotiate the SDP. 2182 */ 2183 neg_state = pjmedia_sdp_neg_get_state(inv->neg); 2184 if (neg_state != PJMEDIA_SDP_NEG_STATE_WAIT_NEGO || 2185 (status=inv_negotiate_sdp(inv)) != PJ_SUCCESS) 2186 { 2187 /* Negotiation has failed */ 2188 status = pjsip_dlg_create_response(inv->dlg, rdata, 2189 PJSIP_SC_NOT_ACCEPTABLE_HERE, 2190 NULL, &tdata); 2191 } else { 2192 /* New media has been negotiated successfully, send 200/OK */ 2193 status = pjsip_dlg_create_response(inv->dlg, rdata, 2194 PJSIP_SC_OK, NULL, &tdata); 2195 if (status == PJ_SUCCESS) { 2196 pjmedia_sdp_session *sdp; 2197 status = pjmedia_sdp_neg_get_active_local(inv->neg, &sdp); 2198 if (status == PJ_SUCCESS) 2199 tdata->msg->body = create_sdp_body(tdata->pool, sdp); 2200 } 2201 } 2202 } 2203 2204 if (status != PJ_SUCCESS) { 2205 if (tdata != NULL) { 2206 pjsip_tx_data_dec_ref(tdata); 2207 tdata = NULL; 2208 } 2209 return; 2210 } 2211 2212 pjsip_dlg_send_response(inv->dlg, pjsip_rdata_get_tsx(rdata), tdata); 2213 } 2214 2215 2216 /* 2217 * Handle incoming response to UAC UPDATE request. 2218 */ 2219 static void inv_handle_update_response( pjsip_inv_session *inv, 2220 pjsip_event *e) 2221 { 2222 pjsip_transaction *tsx = e->body.tsx_state.tsx; 2223 struct tsx_inv_data *tsx_inv_data = NULL; 2224 pj_status_t status = -1; 2225 2226 /* Process 2xx response */ 2227 if (tsx->state == PJSIP_TSX_STATE_COMPLETED && 2228 tsx->status_code/100 == 2 && 2229 e->body.tsx_state.src.rdata->msg_info.msg->body) 2230 { 2231 status = inv_check_sdp_in_incoming_msg(inv, tsx, 2232 e->body.tsx_state.src.rdata); 2233 2234 } else { 2235 /* Get/attach invite session's transaction data */ 2236 tsx_inv_data = (struct tsx_inv_data*)tsx->mod_data[mod_inv.mod.id]; 2237 if (tsx_inv_data == NULL) { 2238 tsx_inv_data=PJ_POOL_ZALLOC_T(tsx->pool, struct tsx_inv_data); 2239 tsx_inv_data->inv = inv; 2240 tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; 2241 } 2242 } 2243 2244 /* Otherwise if we don't get successful response, cancel 2245 * our negotiator. 2246 */ 2247 if (status != PJ_SUCCESS && 2248 pjmedia_sdp_neg_get_state(inv->neg)==PJMEDIA_SDP_NEG_STATE_LOCAL_OFFER && 2249 tsx_inv_data && tsx_inv_data->sdp_done == PJ_FALSE) 2250 { 2251 pjmedia_sdp_neg_cancel_offer(inv->neg); 2252 2253 /* Prevent from us cancelling different offer! */ 2254 tsx_inv_data->sdp_done = PJ_TRUE; 2255 } 2256 } 2257 2258 2259 /* 2260 * Handle incoming reliable response. 2261 */ 2262 static void inv_handle_incoming_reliable_response(pjsip_inv_session *inv, 2263 pjsip_rx_data *rdata) 2264 { 2265 pjsip_tx_data *tdata; 2266 pjmedia_sdp_session *sdp; 2267 pj_status_t status; 2268 2269 /* Create PRACK */ 2270 status = pjsip_100rel_create_prack(inv, rdata, &tdata); 2271 if (status != PJ_SUCCESS) 2272 return; 2273 2274 /* See if we need to attach SDP answer on the PRACK request */ 2275 sdp = inv_has_pending_answer(inv, pjsip_rdata_get_tsx(rdata)); 2276 if (sdp) { 2277 tdata->msg->body = create_sdp_body(tdata->pool, sdp); 2278 } 2279 2280 /* Send PRACK (must be using 100rel module!) */ 2281 pjsip_100rel_send_prack(inv, tdata); 2282 } 2283 2284 2285 /* 2286 * Handle incoming PRACK. 2287 */ 2288 static void inv_respond_incoming_prack(pjsip_inv_session *inv, 2289 pjsip_rx_data *rdata) 2290 { 2291 pj_status_t status; 2292 2293 /* Run through 100rel module to see if we can accept this 2294 * PRACK request. The 100rel will send 200/OK to PRACK request. 2295 */ 2296 status = pjsip_100rel_on_rx_prack(inv, rdata); 2297 if (status != PJ_SUCCESS) 2298 return; 2299 2300 /* Now check for SDP answer in the PRACK request */ 2301 if (rdata->msg_info.msg->body) { 2302 status = inv_check_sdp_in_incoming_msg(inv, 2303 pjsip_rdata_get_tsx(rdata), rdata); 2304 } else { 2305 /* No SDP body */ 2306 status = -1; 2307 } 2308 2309 /* If SDP negotiation has been successful, also mark the 2310 * SDP negotiation flag in the invite transaction to be 2311 * done too. 2312 */ 2313 if (status == PJ_SUCCESS && inv->invite_tsx) { 2314 struct tsx_inv_data *tsx_inv_data; 2315 2316 /* Get/attach invite session's transaction data */ 2317 tsx_inv_data = (struct tsx_inv_data*) 2318 inv->invite_tsx->mod_data[mod_inv.mod.id]; 2319 if (tsx_inv_data == NULL) { 2320 tsx_inv_data = PJ_POOL_ZALLOC_T(inv->invite_tsx->pool, 2321 struct tsx_inv_data); 2322 tsx_inv_data->inv = inv; 2323 inv->invite_tsx->mod_data[mod_inv.mod.id] = tsx_inv_data; 2324 } 2325 2326 tsx_inv_data->sdp_done = PJ_TRUE; 2327 } 2328 } 2329 2330 2331 /* 1990 2332 * State NULL is before anything is sent/received. 1991 2333 */ … … 2073 2415 e->body.tsx_state.src.rdata); 2074 2416 2417 if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) { 2418 inv_handle_incoming_reliable_response( 2419 inv, e->body.tsx_state.src.rdata); 2420 } 2421 2075 2422 } else { 2076 2423 /* Ignore 100 (Trying) response, as it doesn't change … … 2167 2514 } 2168 2515 2169 } else if (inv->role == PJSIP_ROLE_UAC && 2170 tsx->role == PJSIP_ROLE_UAC && 2171 tsx->method.id == PJSIP_CANCEL_METHOD) 2172 { 2516 } else if (tsx->role == PJSIP_ROLE_UAC) { 2173 2517 /* 2174 * Handle case when outgoing CANCELis answered with 481 (Call/2518 * Handle case when outgoing request is answered with 481 (Call/ 2175 2519 * Transaction Does Not Exist), 408, or when it's timed out. In these 2176 2520 * cases, disconnect session (i.e. dialog usage only). … … 2285 2629 inv_check_sdp_in_incoming_msg(inv, tsx, 2286 2630 e->body.tsx_state.src.rdata); 2631 2632 if (pjsip_100rel_is_reliable(e->body.tsx_state.src.rdata)) { 2633 inv_handle_incoming_reliable_response( 2634 inv, e->body.tsx_state.src.rdata); 2635 } 2287 2636 } 2288 2637 break; … … 2354 2703 inv_respond_incoming_cancel(inv, tsx, e->body.tsx_state.src.rdata); 2355 2704 2356 } else if ( inv->role == PJSIP_ROLE_UAC&&2357 tsx-> role == PJSIP_ROLE_UAC&&2358 tsx->method.id == PJSIP_CANCEL_METHOD)2705 } else if (tsx->role == PJSIP_ROLE_UAS && 2706 tsx->state == PJSIP_TSX_STATE_TRYING && 2707 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 2359 2708 { 2360 2709 /* 2361 * Handle case when outgoing CANCEL is answered with 481 (Call/ 2710 * Handle incoming UPDATE 2711 */ 2712 inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); 2713 2714 2715 } else if (tsx->role == PJSIP_ROLE_UAC && 2716 (tsx->state == PJSIP_TSX_STATE_COMPLETED || 2717 tsx->state == PJSIP_TSX_STATE_TERMINATED) && 2718 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 2719 { 2720 /* 2721 * Handle response to outgoing UPDATE request. 2722 */ 2723 inv_handle_update_response(inv, e); 2724 2725 } else if (tsx->role == PJSIP_ROLE_UAS && 2726 tsx->state == PJSIP_TSX_STATE_TRYING && 2727 pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) 2728 { 2729 /* 2730 * Handle incoming PRACK 2731 */ 2732 inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); 2733 2734 } else if (tsx->role == PJSIP_ROLE_UAC) { 2735 /* 2736 * Handle case when outgoing request is answered with 481 (Call/ 2362 2737 * Transaction Does Not Exist), 408, or when it's timed out. In these 2363 2738 * cases, disconnect session (i.e. dialog usage only). … … 2467 2842 if (status != PJ_SUCCESS) return; 2468 2843 2469 } 2844 } else if (tsx->role == PJSIP_ROLE_UAS && 2845 tsx->state == PJSIP_TSX_STATE_TRYING && 2846 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 2847 { 2848 /* 2849 * Handle incoming UPDATE 2850 */ 2851 inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); 2852 2853 2854 } else if (tsx->role == PJSIP_ROLE_UAC && 2855 (tsx->state == PJSIP_TSX_STATE_COMPLETED || 2856 tsx->state == PJSIP_TSX_STATE_TERMINATED) && 2857 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 2858 { 2859 /* 2860 * Handle response to outgoing UPDATE request. 2861 */ 2862 inv_handle_update_response(inv, e); 2863 2864 } else if (tsx->role == PJSIP_ROLE_UAS && 2865 tsx->state == PJSIP_TSX_STATE_TRYING && 2866 pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) 2867 { 2868 /* 2869 * Handle incoming PRACK 2870 */ 2871 inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); 2872 2873 } else if (tsx->role == PJSIP_ROLE_UAC) { 2874 /* 2875 * Handle case when outgoing request is answered with 481 (Call/ 2876 * Transaction Does Not Exist), 408, or when it's timed out. In these 2877 * cases, disconnect session (i.e. dialog usage only). 2878 */ 2879 if (tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST || 2880 tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT || 2881 tsx->status_code == PJSIP_SC_TSX_TIMEOUT || 2882 tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR) 2883 { 2884 inv_set_cause(inv, tsx->status_code, &tsx->status_text); 2885 inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 2886 } 2887 } 2888 2470 2889 } 2471 2890 … … 2731 3150 } 2732 3151 } 2733 } 3152 3153 } else if (tsx->role == PJSIP_ROLE_UAS && 3154 tsx->state == PJSIP_TSX_STATE_TRYING && 3155 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 3156 { 3157 /* 3158 * Handle incoming UPDATE 3159 */ 3160 inv_respond_incoming_update(inv, e->body.tsx_state.src.rdata); 3161 3162 } else if (tsx->role == PJSIP_ROLE_UAC && 3163 (tsx->state == PJSIP_TSX_STATE_COMPLETED || 3164 tsx->state == PJSIP_TSX_STATE_TERMINATED) && 3165 pjsip_method_cmp(&tsx->method, &pjsip_update_method)==0) 3166 { 3167 /* 3168 * Handle response to outgoing UPDATE request. 3169 */ 3170 inv_handle_update_response(inv, e); 3171 3172 } else if (tsx->role == PJSIP_ROLE_UAS && 3173 tsx->state == PJSIP_TSX_STATE_TRYING && 3174 pjsip_method_cmp(&tsx->method, &pjsip_prack_method)==0) 3175 { 3176 /* 3177 * Handle strandled incoming PRACK 3178 */ 3179 inv_respond_incoming_prack(inv, e->body.tsx_state.src.rdata); 3180 3181 } else if (tsx->role == PJSIP_ROLE_UAC) { 3182 /* 3183 * Handle case when outgoing request is answered with 481 (Call/ 3184 * Transaction Does Not Exist), 408, or when it's timed out. In these 3185 * cases, disconnect session (i.e. dialog usage only). 3186 */ 3187 if (tsx->status_code == PJSIP_SC_CALL_TSX_DOES_NOT_EXIST || 3188 tsx->status_code == PJSIP_SC_REQUEST_TIMEOUT || 3189 tsx->status_code == PJSIP_SC_TSX_TIMEOUT || 3190 tsx->status_code == PJSIP_SC_TSX_TRANSPORT_ERROR) 3191 { 3192 inv_set_cause(inv, tsx->status_code, &tsx->status_text); 3193 inv_set_state(inv, PJSIP_INV_STATE_DISCONNECTED, e); 3194 } 3195 } 3196 2734 3197 } 2735 3198 -
pjproject/trunk/pjsip/src/pjsip/sip_dialog.c
r1466 r1469 1201 1201 } 1202 1202 1203 /* Add Allow header in 2xx and 405 response. */1204 if ((( st_class==2&& dlg->add_allow)1203 /* Add Allow header in 18x, 2xx and 405 response. */ 1204 if ((((st_code/10==18 || st_class==2) && dlg->add_allow) 1205 1205 || st_code==405) && 1206 1206 pjsip_msg_find_hdr(tdata->msg, PJSIP_H_ALLOW, NULL)==NULL) -
pjproject/trunk/pjsip/src/pjsip/sip_transaction.c
r1463 r1469 905 905 pj_ansi_snprintf(tsx->obj_name, sizeof(tsx->obj_name), 906 906 "tsx%p", tsx); 907 pj_memcpy(pool->obj_name, tsx->obj_name, sizeof(pool->obj_name)); 907 908 908 909 tsx->handle_200resp = 1; -
pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
r1463 r1469 196 196 197 197 198 #define LATE_SDP 0 199 198 200 /* 199 201 * Make outgoing call to the specified URI using the specified account. … … 313 315 314 316 /* Create SDP offer */ 317 #if LATE_SDP 318 offer = NULL; 319 #else 315 320 status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer); 316 321 if (status != PJ_SUCCESS) { … … 318 323 goto on_error; 319 324 } 325 #endif 320 326 321 327 /* Create the INVITE session: */ -
pjproject/trunk/pjsip/src/test-pjsip/test.c
r1159 r1469 356 356 #endif 357 357 358 #if INCLUDE_INV_OA_TEST 359 DO_TEST(inv_offer_answer_test()); 360 #endif 361 358 362 359 363 on_return: -
pjproject/trunk/pjsip/src/test-pjsip/test.h
r974 r1469 40 40 #define INCLUDE_TRANSPORT_GROUP 1 41 41 #define INCLUDE_TSX_GROUP 1 42 #define INCLUDE_INV_GROUP 1 42 43 43 44 /* … … 59 60 #define INCLUDE_RESOLVE_TEST INCLUDE_TRANSPORT_GROUP 60 61 #define INCLUDE_TSX_TEST INCLUDE_TSX_GROUP 61 62 #define INCLUDE_INV_OA_TEST INCLUDE_INV_GROUP 62 63 63 64 … … 95 96 int *pkt_lost); 96 97 98 /* Invite session */ 99 int inv_offer_answer_test(void); 100 97 101 /* Test main entry */ 98 102 int test_main(void);
Note: See TracChangeset
for help on using the changeset viewer.