File: | libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c |
Location: | line 1617, column 9 |
Description: | Access to field 'du_cr' results in a dereference of a null pointer (loaded from variable 'du') |
1 | /* | |||||
2 | * This file is part of the Sofia-SIP package | |||||
3 | * | |||||
4 | * Copyright (C) 2006 Nokia Corporation. | |||||
5 | * | |||||
6 | * Contact: Pekka Pessi <pekka.pessi@nokia.com> | |||||
7 | * | |||||
8 | * This library is free software; you can redistribute it and/or | |||||
9 | * modify it under the terms of the GNU Lesser General Public License | |||||
10 | * as published by the Free Software Foundation; either version 2.1 of | |||||
11 | * the License, or (at your option) any later version. | |||||
12 | * | |||||
13 | * This library is distributed in the hope that it will be useful, but | |||||
14 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |||||
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||||
16 | * Lesser General Public License for more details. | |||||
17 | * | |||||
18 | * You should have received a copy of the GNU Lesser General Public | |||||
19 | * License along with this library; if not, write to the Free Software | |||||
20 | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA | |||||
21 | * 02110-1301 USA | |||||
22 | * | |||||
23 | */ | |||||
24 | ||||||
25 | /**@CFILE nua_session.c | |||||
26 | * @brief SIP session handling | |||||
27 | * | |||||
28 | * @author Pekka Pessi <Pekka.Pessi@nokia.com> | |||||
29 | * | |||||
30 | * @date Created: Wed Mar 8 16:17:27 EET 2006 ppessi | |||||
31 | */ | |||||
32 | ||||||
33 | #include "config.h" | |||||
34 | ||||||
35 | #include <stddef.h> | |||||
36 | #include <stdlib.h> | |||||
37 | #include <string.h> | |||||
38 | #include <limits.h> | |||||
39 | ||||||
40 | #include <assert.h> | |||||
41 | ||||||
42 | #include <sofia-sip/su_string.h> | |||||
43 | #include <sofia-sip/sip_protos.h> | |||||
44 | #include <sofia-sip/sip_status.h> | |||||
45 | #include <sofia-sip/sip_util.h> | |||||
46 | #include <sofia-sip/su_uniqueid.h> | |||||
47 | #include <sofia-sip/msg_mime_protos.h> | |||||
48 | ||||||
49 | #define NTA_INCOMING_MAGIC_Tstruct nua_server_request struct nua_server_request | |||||
50 | #define NTA_OUTGOING_MAGIC_Tstruct nua_client_request struct nua_client_request | |||||
51 | #define NTA_RELIABLE_MAGIC_Tstruct nua_server_request struct nua_server_request | |||||
52 | ||||||
53 | #include "nua_stack.h" | |||||
54 | #include <sofia-sip/soa.h> | |||||
55 | ||||||
56 | #ifndef SDP_H | |||||
57 | typedef struct sdp_session_s sdp_session_t; | |||||
58 | #endif | |||||
59 | ||||||
60 | /* ---------------------------------------------------------------------- */ | |||||
61 | ||||||
62 | /** @enum nua_callstate | |||||
63 | ||||||
64 | The states for SIP session established with INVITE. | |||||
65 | ||||||
66 | Initially the call states follow the state of the INVITE transaction. If the | |||||
67 | initial INVITE transaction fails, the call is terminated. The status codes | |||||
68 | 401 and 407 are an exception: if the client (on the left side in the diagram | |||||
69 | below) receives them, it enters in #nua_callstate_authenticating state. | |||||
70 | ||||||
71 | If a re-INVITE transaction fails, the result depends on the status code in | |||||
72 | failure. The call can return to the ready state, be terminated immediately, | |||||
73 | or be terminated gracefully. The proper action to take is determined with | |||||
74 | sip_response_terminates_dialog(). | |||||
75 | ||||||
76 | @sa @ref nua_call_model, #nua_i_state, nua_invite(), #nua_i_invite | |||||
77 | ||||||
78 | @par Session State Diagram | |||||
79 | ||||||
80 | @code | |||||
81 | +----------+ | |||||
82 | | |---------------------+ | |||||
83 | | Init | | | |||||
84 | | |----------+ | | |||||
85 | +----------+ | | | |||||
86 | | | | | | |||||
87 | --/INVITE| |INVITE/100 | | | |||||
88 | V V | | | |||||
89 | +----------+ +----------+ | | | |||||
90 | +--------| | | | | | | |||||
91 | | 18X +-| Calling | | Received | |INVITE/ | | |||||
92 | | /- | | | | | | /18X | | |||||
93 | | V +----------+ +----------+ V | | |||||
94 | | +----------+ | | | +----------+ | | |||||
95 | |---| | |2XX -/ | -/ | | | | | |||||
96 | | | Proceed- | | /- 2XX| 18X| | Early | |INVITE/ | |||||
97 | | | ing | | | +->| | | /200 | |||||
98 | | +----------+ V V +----------+ | | |||||
99 | | | +----------+ +----------+ | -/ | | |||||
100 | | 2XX| | | | |<--+ 2XX | | |||||
101 | | /-| | Complet- | | Complete |<-----------+ | |||||
102 | | +->| ing | | |------+ | |||||
103 | | +----------+ +----------+ | | |||||
104 | | | | | | | |||||
105 | |401,407/ -/ACK| |ACK/- |timeout/ | | |||||
106 | | /ACK V V | /BYE | | |||||
107 | | +----------+ | | | |||||
108 | | | | | | | |||||
109 | | +--| Ready | | | | |||||
110 | | | | | | | | |||||
111 | | | +----------+ | | | |||||
112 | | | | | | | |||||
113 | | BYE/ | |-/BYE | |BYE/ | |||||
114 | V /200 | V | |/200 | |||||
115 | +----------+ | +----------+ | | | |||||
116 | | | | | | | | | |||||
117 | |Authentic-| | | Terminat-|<----+ | | |||||
118 | | ating | | | ing | | | |||||
119 | +----------+ | +----------+ | | |||||
120 | | | | | |||||
121 | | |[23456]XX/- | | |||||
122 | | V | | |||||
123 | | +----------+ | | |||||
124 | | | | | | |||||
125 | +->|Terminated|<--------------+ | |||||
126 | | | | |||||
127 | +----------+ | |||||
128 | | | |||||
129 | V | |||||
130 | +----------+ | |||||
131 | | | | |||||
132 | | Init | | |||||
133 | | | | |||||
134 | +----------+ | |||||
135 | @endcode | |||||
136 | */ | |||||
137 | ||||||
138 | /* ---------------------------------------------------------------------- */ | |||||
139 | /* Session event usage */ | |||||
140 | ||||||
141 | /** @internal @brief Session-related state. */ | |||||
142 | typedef struct nua_session_usage | |||||
143 | { | |||||
144 | enum nua_callstate ss_state; /**< Session status (enum nua_callstate) */ | |||||
145 | ||||||
146 | unsigned ss_100rel:1; /**< Use 100rel, send 183 */ | |||||
147 | unsigned ss_alerting:1; /**< 180 is sent/received */ | |||||
148 | ||||||
149 | unsigned ss_update_needed:2; /**< Send an UPDATE (do O/A if > 1) */ | |||||
150 | ||||||
151 | unsigned ss_precondition:1; /**< Precondition required */ | |||||
152 | ||||||
153 | unsigned ss_reporting:1; /**< True if reporting state */ | |||||
154 | unsigned : 0; | |||||
155 | ||||||
156 | struct session_timer { | |||||
157 | unsigned interval; /**< Negotiated expiration time */ | |||||
158 | enum nua_session_refresher refresher; /**< Our Negotiated role */ | |||||
159 | ||||||
160 | struct { | |||||
161 | unsigned expires, defaults; /**< Value of Session-Expires (delta) */ | |||||
162 | unsigned min_se; /**< Minimum session expires */ | |||||
163 | /** none, local or remote */ | |||||
164 | enum nua_session_refresher refresher; | |||||
165 | unsigned supported:1, require:1, :0; | |||||
166 | } local, remote; | |||||
167 | ||||||
168 | unsigned timer_set:1; /**< We have active session timer. */ | |||||
169 | } ss_timer[1]; | |||||
170 | ||||||
171 | char const *ss_reason; /**< Reason for termination. */ | |||||
172 | ||||||
173 | /* Offer-Answer status */ | |||||
174 | char const *ss_oa_recv, *ss_oa_sent; | |||||
175 | ||||||
176 | /**< Version of user SDP from latest successful O/A */ | |||||
177 | int ss_sdp_version; | |||||
178 | } nua_session_usage_t; | |||||
179 | ||||||
180 | static char const Offer[] = "offer", Answer[] = "answer"; | |||||
181 | ||||||
182 | static char const *nua_session_usage_name(nua_dialog_usage_t const *du); | |||||
183 | static int nua_session_usage_add(nua_handle_t *nh, | |||||
184 | nua_dialog_state_t *ds, | |||||
185 | nua_dialog_usage_t *du); | |||||
186 | static void nua_session_usage_remove(nua_handle_t *nh, | |||||
187 | nua_dialog_state_t *ds, | |||||
188 | nua_dialog_usage_t *du, | |||||
189 | nua_client_request_t *cr, | |||||
190 | nua_server_request_t *sr); | |||||
191 | static void nua_session_usage_refresh(nua_owner_t *, | |||||
192 | nua_dialog_state_t *, | |||||
193 | nua_dialog_usage_t *, | |||||
194 | sip_time_t now); | |||||
195 | static int nua_session_usage_shutdown(nua_owner_t *, | |||||
196 | nua_dialog_state_t *, | |||||
197 | nua_dialog_usage_t *); | |||||
198 | ||||||
199 | static void signal_call_state_change(nua_handle_t *nh, | |||||
200 | nua_session_usage_t *ss, | |||||
201 | int status, char const *phrase, | |||||
202 | enum nua_callstate next_state); | |||||
203 | ||||||
204 | static int nua_invite_client_should_ack(nua_client_request_t const *cr); | |||||
205 | static int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags); | |||||
206 | static int nua_invite_client_complete(nua_client_request_t *cr); | |||||
207 | ||||||
208 | static nua_usage_class const nua_session_usage[1] = { | |||||
209 | { | |||||
210 | sizeof (nua_session_usage_t), | |||||
211 | sizeof nua_session_usage, | |||||
212 | nua_session_usage_add, | |||||
213 | nua_session_usage_remove, | |||||
214 | nua_session_usage_name, | |||||
215 | nua_base_usage_update_params, | |||||
216 | NULL((void*)0), | |||||
217 | nua_session_usage_refresh, | |||||
218 | nua_session_usage_shutdown | |||||
219 | }}; | |||||
220 | ||||||
221 | static char const *nua_session_usage_name(nua_dialog_usage_t const *du) | |||||
222 | { | |||||
223 | return "session"; | |||||
224 | } | |||||
225 | ||||||
226 | static | |||||
227 | int nua_session_usage_add(nua_handle_t *nh, | |||||
228 | nua_dialog_state_t *ds, | |||||
229 | nua_dialog_usage_t *du) | |||||
230 | { | |||||
231 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
232 | ||||||
233 | if (ds->ds_has_session) | |||||
234 | return -1; | |||||
235 | ds->ds_has_session = 1; | |||||
236 | ds->ds_got_session = 1; | |||||
237 | ||||||
238 | ss->ss_timer->local.refresher = nua_any_refresher; | |||||
239 | ss->ss_timer->remote.refresher = nua_any_refresher; | |||||
240 | ||||||
241 | return 0; | |||||
242 | } | |||||
243 | ||||||
244 | static | |||||
245 | void nua_session_usage_remove(nua_handle_t *nh, | |||||
246 | nua_dialog_state_t *ds, | |||||
247 | nua_dialog_usage_t *du, | |||||
248 | nua_client_request_t *cr0, | |||||
249 | nua_server_request_t *sr0) | |||||
250 | { | |||||
251 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
252 | nua_client_request_t *cr, *cr_next; | |||||
253 | nua_server_request_t *sr; | |||||
254 | ||||||
255 | /* Destroy queued INVITE transactions */ | |||||
256 | for (cr = ds->ds_cr; cr; cr = cr_next) { | |||||
257 | cr_next = cr->cr_next; | |||||
258 | ||||||
259 | if (cr->cr_method != sip_method_invite) | |||||
260 | continue; | |||||
261 | ||||||
262 | if (cr == cr0) | |||||
263 | continue; | |||||
264 | ||||||
265 | nua_client_request_ref(cr); | |||||
266 | ||||||
267 | if (nua_invite_client_should_ack(cr)) { | |||||
268 | ss->ss_reporting = 1; | |||||
269 | nua_invite_client_ack(cr, NULL((void*)0)); | |||||
270 | ss->ss_reporting = 0; | |||||
271 | } | |||||
272 | ||||||
273 | if (cr == du->du_cr && cr->cr_orq) { | |||||
274 | nua_client_request_unref(cr); | |||||
275 | continue; | |||||
276 | } | |||||
277 | ||||||
278 | if (cr->cr_status < 200) { | |||||
279 | nua_stack_event(nh->nh_nua, nh, | |||||
280 | NULL((void*)0), | |||||
281 | (enum nua_event_e)cr->cr_event, | |||||
282 | SIP_481_NO_TRANSACTION481, sip_481_No_transaction, | |||||
283 | NULL((void*)0)); | |||||
284 | } | |||||
285 | ||||||
286 | nua_client_request_remove(cr); | |||||
287 | ||||||
288 | nua_client_request_unref(cr); | |||||
289 | ||||||
290 | cr_next = ds->ds_cr; | |||||
291 | } | |||||
292 | ||||||
293 | if (ss->ss_state != nua_callstate_terminated && | |||||
294 | ss->ss_state != nua_callstate_init && | |||||
295 | !ss->ss_reporting) { | |||||
296 | int status = 0; char const *phrase = "Terminated"; | |||||
297 | ||||||
298 | if (cr0) | |||||
299 | status = cr0->cr_status, phrase = cr0->cr_phrase ? cr0->cr_phrase : phrase; | |||||
300 | else if (sr0) | |||||
301 | status = sr0->sr_status, phrase = sr0->sr_phrase; | |||||
302 | ||||||
303 | signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated); | |||||
304 | } | |||||
305 | ||||||
306 | /* Application can respond to BYE after the session usage has terminated */ | |||||
307 | for (sr = ds->ds_sr; sr; sr = sr->sr_next) { | |||||
308 | if (sr->sr_usage == du && sr->sr_method == sip_method_bye) | |||||
309 | sr->sr_usage = NULL((void*)0); | |||||
310 | } | |||||
311 | ||||||
312 | ds->ds_has_session = 0; | |||||
313 | nh->nh_has_invite = 0; | |||||
314 | nh->nh_active_call = 0; | |||||
315 | nh->nh_hold_remote = 0; | |||||
316 | ||||||
317 | if (nh->nh_soa) | |||||
318 | soa_destroy(nh->nh_soa), nh->nh_soa = NULL((void*)0); | |||||
319 | } | |||||
320 | ||||||
321 | static | |||||
322 | nua_dialog_usage_t *nua_dialog_usage_for_session(nua_dialog_state_t const *ds) | |||||
323 | { | |||||
324 | if (ds == ((nua_handle_t *)NULL((void*)0))->nh_ds) | |||||
325 | return NULL((void*)0); | |||||
326 | ||||||
327 | return nua_dialog_usage_get(ds, nua_session_usage, NULL((void*)0)); | |||||
328 | } | |||||
329 | ||||||
330 | static | |||||
331 | nua_session_usage_t *nua_session_usage_for_dialog(nua_dialog_state_t const *ds) | |||||
332 | { | |||||
333 | nua_dialog_usage_t *du; | |||||
334 | ||||||
335 | if (ds == ((nua_handle_t *)NULL((void*)0))->nh_ds) | |||||
336 | return NULL((void*)0); | |||||
337 | ||||||
338 | du = nua_dialog_usage_get(ds, nua_session_usage, NULL((void*)0)); | |||||
339 | ||||||
340 | return (nua_session_usage_t *)nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
341 | } | |||||
342 | ||||||
343 | /** Zap the session associated with the handle */ | |||||
344 | static | |||||
345 | void nua_session_usage_destroy(nua_handle_t *nh, | |||||
346 | nua_session_usage_t *ss) | |||||
347 | { | |||||
348 | /* Remove usage */ | |||||
349 | nua_dialog_usage_remove(nh, nh->nh_ds, nua_dialog_usage_public(ss)((ss) ? (nua_dialog_usage_t*)(ss) - 1 : ((void*)0)), NULL((void*)0), NULL((void*)0)); | |||||
350 | ||||||
351 | SU_DEBUG_5(("nua: terminated session %p\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 351, "nua: terminated session %p\n" , (void *)nh)) : (void)0); | |||||
352 | } | |||||
353 | ||||||
354 | /* ======================================================================== */ | |||||
355 | /* INVITE and call (session) processing */ | |||||
356 | ||||||
357 | static int session_timer_is_supported(struct session_timer const *t); | |||||
358 | ||||||
359 | static void session_timer_preferences(struct session_timer *t, | |||||
360 | sip_t const *sip, | |||||
361 | sip_supported_t const *supported, | |||||
362 | unsigned expires, int isset, | |||||
363 | enum nua_session_refresher refresher, | |||||
364 | unsigned min_se); | |||||
365 | ||||||
366 | static void session_timer_store(struct session_timer *t, | |||||
367 | sip_t const *sip); | |||||
368 | ||||||
369 | static int session_timer_check_min_se(msg_t *msg, sip_t *sip, | |||||
370 | sip_t const *request, | |||||
371 | unsigned long min_se); | |||||
372 | ||||||
373 | static int session_timer_add_headers(struct session_timer *t, | |||||
374 | int initial, | |||||
375 | msg_t *msg, sip_t *sip, | |||||
376 | nua_handle_t *nh); | |||||
377 | ||||||
378 | static void session_timer_negotiate(struct session_timer *t, int uas); | |||||
379 | ||||||
380 | static void session_timer_set(nua_session_usage_t *ss, int uas); | |||||
381 | ||||||
382 | static int session_timer_check_restart(nua_client_request_t *cr, | |||||
383 | int status, char const *phrase, | |||||
384 | sip_t const *sip); | |||||
385 | ||||||
386 | static int nh_referral_check(nua_handle_t *nh, tagi_t const *tags); | |||||
387 | static void nh_referral_respond(nua_handle_t *, | |||||
388 | int status, char const *phrase); | |||||
389 | ||||||
390 | static | |||||
391 | int session_get_description(sip_t const *sip, | |||||
392 | char const **return_sdp, | |||||
393 | size_t *return_len); | |||||
394 | ||||||
395 | static | |||||
396 | int session_include_description(soa_session_t *soa, | |||||
397 | int session, | |||||
398 | msg_t *msg, | |||||
399 | sip_t *sip); | |||||
400 | ||||||
401 | static | |||||
402 | int session_make_description(su_home_t *home, | |||||
403 | soa_session_t *soa, | |||||
404 | int session, | |||||
405 | sip_content_disposition_t **return_cd, | |||||
406 | sip_content_type_t **return_ct, | |||||
407 | sip_payload_t **return_pl); | |||||
408 | ||||||
409 | static | |||||
410 | int nua_server_retry_after(nua_server_request_t *sr, | |||||
411 | int status, char const *phrase, | |||||
412 | int min, int max); | |||||
413 | ||||||
414 | /**@fn void nua_invite(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
415 | * | |||||
416 | * Place a call using SIP @b INVITE method. | |||||
417 | * | |||||
418 | * The INVITE method is used to initiate a call between two parties. The | |||||
419 | * call is also known as <i>SIP session</i>. | |||||
420 | * | |||||
421 | * At SIP level the session is represented as @e Dialog, which is a | |||||
422 | * peer-to-peer association between two SIP User-Agents. The dialog is | |||||
423 | * established by a successful 2XX response to the INVITE. The dialog is | |||||
424 | * terminated by BYE transaction, which application can initiate with | |||||
425 | * nua_bye() call. | |||||
426 | * | |||||
427 | * An @e early @e dialog is established by an preliminary response | |||||
428 | * (101..199), such as <i>180 Ringing</i>. An early dialog is terminated | |||||
429 | * with an error response with response code in range 300...699. | |||||
430 | * | |||||
431 | * The media session belonging to the SIP session is usually represented by | |||||
432 | * SDP, Session Description Protocol. The media session it is usually | |||||
433 | * established during the call set-up with procedure known as SDP | |||||
434 | * Offer/Answer exchange, defined by @RFC3264. See <b>Media Session | |||||
435 | * Handling</b> below for details. | |||||
436 | * | |||||
437 | * @param nh Pointer to operation handle | |||||
438 | * @param tag, value, ... List of tagged parameters | |||||
439 | * | |||||
440 | * @return | |||||
441 | * nothing | |||||
442 | * | |||||
443 | * @par Events: | |||||
444 | * #nua_r_invite \n | |||||
445 | * #nua_i_state (#nua_i_active, #nua_i_terminated) \n | |||||
446 | * #nua_i_media_error \n | |||||
447 | * #nua_i_fork \n | |||||
448 | * | |||||
449 | * @par Tags: | |||||
450 | * NUTAG_AUTH_CACHE() \n | |||||
451 | * NUTAG_AUTOACK() \n | |||||
452 | * NUTAG_AUTOANSWER() \n | |||||
453 | * NUTAG_EARLY_MEDIA() \n | |||||
454 | * NUTAG_ENABLEINVITE() \n | |||||
455 | * NUTAG_INITIAL_ROUTE(), NUTAG_INITIAL_ROUTE_STR() \n | |||||
456 | * NUTAG_INVITE_TIMER() \n | |||||
457 | * NUTAG_MEDIA_ENABLE() \n | |||||
458 | * NUTAG_MEDIA_FEATURES() \n | |||||
459 | * NUTAG_MIN_SE() \n | |||||
460 | * NUTAG_RETRY_COUNT() \n | |||||
461 | * NUTAG_SERVICE_ROUTE_ENABLE() \n | |||||
462 | * NUTAG_SESSION_REFRESHER() \n | |||||
463 | * NUTAG_SESSION_TIMER() \n | |||||
464 | * NUTAG_SOA_NAME() \n | |||||
465 | * NUTAG_UPDATE_REFRESH() \n | |||||
466 | * | |||||
467 | * @par Populating SIP Request Message with Tagged Arguments | |||||
468 | * The tagged arguments can be used to pass values for any SIP headers to | |||||
469 | * the stack. When the INVITE message (or any other SIP message) is created, | |||||
470 | * the tagged values saved with nua_handle() are used first, next the tagged | |||||
471 | * values given with the operation (nua_invite()) are added. | |||||
472 | * | |||||
473 | * @par | |||||
474 | * When multiple tags for the same header are specified, the behaviour | |||||
475 | * depends on the header type. If only a single header field can be included | |||||
476 | * in a SIP message, the latest non-NULL value is used, e.g., @Subject. | |||||
477 | * However, if the SIP header can consist of multiple lines or header fields | |||||
478 | * separated by comma, e.g., @Accept, all the tagged | |||||
479 | * values are concatenated. | |||||
480 | * | |||||
481 | * @par | |||||
482 | * However, if a tag value is #SIP_NONE (-1 casted as a void pointer), the | |||||
483 | * values from previous tags are ignored. | |||||
484 | * | |||||
485 | * @par | |||||
486 | * Next, values previously set with nua_set_params() or nua_set_hparams() | |||||
487 | * are used: @Allow, @Supported, @Organization, and @UserAgent headers are | |||||
488 | * added to the request if they are not already set. | |||||
489 | * | |||||
490 | * @par | |||||
491 | * Now, the target URI for the request needs to be determined. | |||||
492 | * | |||||
493 | * @par | |||||
494 | * For initial INVITE requests, values from tags are used. If NUTAG_URL() is | |||||
495 | * given, it is used as target URI. Otherwise, if SIPTAG_TO() is given, it | |||||
496 | * is used as target URI. If neither is given, the complete request line | |||||
497 | * already specified using SIPTAG_REQUEST() or SIPTAG_REQUEST_STR() is used. | |||||
498 | * If none of the tags above are given, an internal error is returned to the | |||||
499 | * application. At this point, the target URI is stored in the request line, | |||||
500 | * together with method name ("INVITE") and protocol version ("SIP/2.0"). | |||||
501 | * The initial dialog information is also created: @CallID, @CSeq headers | |||||
502 | * are generated, if they do not exist, and an unique tag is added to @From | |||||
503 | * header. | |||||
504 | * | |||||
505 | * @par | |||||
506 | * For the initial INVITE requests, the @Route headers specified by | |||||
507 | * SIPTAG_ROUTE()/SIPTAG_ROUTER_STR() tags in nua_handle() and nua_invite() | |||||
508 | * calls are inserted to the request. Next the initial route set specified | |||||
509 | * by NUTAG_INITIAL_ROUTE()/NUTAG_INITIAL_ROUTE_STR() tags is prepended to | |||||
510 | * the route. Finally (unless NUTAG_SERVICE_ROUTE_ENABLE(0) is used) the | |||||
511 | * @ServiceRoute set received from the registrar is also appended to the | |||||
512 | * route set of the initial request message. | |||||
513 | * | |||||
514 | * @par | |||||
515 | * Next, the stack generates a @Contact header for the request (Unless the | |||||
516 | * application already gave a @Contact header or it does not want to use | |||||
517 | * @Contact and indicates that by including SIPTAG_CONTACT(NULL) or | |||||
518 | * SIPTAG_CONTACT(SIP_NONE) in the tagged parameters.) If the application | |||||
519 | * has a registration active, the @Contact header used with registration is | |||||
520 | * used. Otherwise, the @Contact header is generated from the local IP | |||||
521 | * address and port number, taking also the values from NUTAG_M_DISPLAY(), | |||||
522 | * NUTAG_M_FEATURES(), NUTAG_M_PARAMS(), and NUTAG_M_USERNAME(). | |||||
523 | * | |||||
524 | * @par | |||||
525 | * For in-dialog INVITE (re-INVITE), the request URI is taken from the | |||||
526 | * @Contact header received from the remote party during the dialog | |||||
527 | * establishment. Also, the @CallID and @CSeq headers and @From and @To tags | |||||
528 | * are generated based on the dialog information and added to the request. | |||||
529 | * If the dialog has a route (set by @RecordRoute headers), it is added to | |||||
530 | * the request, too. | |||||
531 | * | |||||
532 | * @par | |||||
533 | * @MaxForwards header (with default value set by NTATAG_MAX_FORWARDS()) is | |||||
534 | * also added now, if it does not exist. | |||||
535 | * | |||||
536 | * @par | |||||
537 | * The INVITE request message created by nua_invite() operation is saved as | |||||
538 | * a template for automatic re-INVITE requests sent by the session timer | |||||
539 | * ("timer") feature (see NUTAG_SESSION_TIMER() for more details). Please | |||||
540 | * note that the template message is not used when ACK, PRACK, UPDATE or | |||||
541 | * INFO requests are created (however, these requests will include | |||||
542 | * dialog-specific headers like @To, @From, and @CallID as well as | |||||
543 | * preference headers @Allow, @Supported, @UserAgent, @Organization). | |||||
544 | * | |||||
545 | * @par Tags Related to SIP Headers and Request-URI | |||||
546 | * NUTAG_URL(), SIPTAG_REQUEST(), SIPTAG_REQUEST_STR() \n | |||||
547 | * NUTAG_INITIAL_ROUTE(), NUTAG_INITIAL_ROUTE_STR(), | |||||
548 | * SIPTAG_ROUTE(), SIPTAG_ROUTE_STR(), | |||||
549 | * NUTAG_SERVICE_ROUTE_ENABLE() \n | |||||
550 | * SIPTAG_MAX_FORWARDS(), SIPTAG_MAX_FORWARDS_STR() \n | |||||
551 | * SIPTAG_PROXY_REQUIRE(), SIPTAG_PROXY_REQUIRE_STR() \n | |||||
552 | * SIPTAG_FROM(), SIPTAG_FROM_STR() \n | |||||
553 | * SIPTAG_TO(), SIPTAG_TO_STR() \n | |||||
554 | * SIPTAG_CALL_ID(), SIPTAG_CALL_ID_STR() \n | |||||
555 | * SIPTAG_CSEQ(), SIPTAG_CSEQ_STR() | |||||
556 | * (note that @CSeq value is incremented if request gets retried)\n | |||||
557 | * SIPTAG_CONTACT(), SIPTAG_CONTACT_STR() \n | |||||
558 | * SIPTAG_REQUEST_DISPOSITION(), SIPTAG_REQUEST_DISPOSITION_STR() \n | |||||
559 | * SIPTAG_ACCEPT_CONTACT(), SIPTAG_ACCEPT_CONTACT_STR() \n | |||||
560 | * SIPTAG_REJECT_CONTACT(), SIPTAG_REJECT_CONTACT_STR() \n | |||||
561 | * SIPTAG_EXPIRES(), SIPTAG_EXPIRES_STR() \n | |||||
562 | * SIPTAG_DATE(), SIPTAG_DATE_STR() \n | |||||
563 | * SIPTAG_TIMESTAMP(), SIPTAG_TIMESTAMP_STR() \n | |||||
564 | * SIPTAG_SUBJECT(), SIPTAG_SUBJECT_STR() \n | |||||
565 | * SIPTAG_PRIORITY(), SIPTAG_PRIORITY_STR() \n | |||||
566 | * SIPTAG_CALL_INFO(), SIPTAG_CALL_INFO_STR() \n | |||||
567 | * SIPTAG_ORGANIZATION(), SIPTAG_ORGANIZATION_STR() \n | |||||
568 | * NUTAG_USER_AGENT(), SIPTAG_USER_AGENT() and SIPTAG_USER_AGENT_STR() \n | |||||
569 | * SIPTAG_IN_REPLY_TO(), SIPTAG_IN_REPLY_TO_STR() \n | |||||
570 | * SIPTAG_ACCEPT(), SIPTAG_ACCEPT_STR() \n | |||||
571 | * SIPTAG_ACCEPT_ENCODING(), SIPTAG_ACCEPT_ENCODING_STR() \n | |||||
572 | * SIPTAG_ACCEPT_LANGUAGE(), SIPTAG_ACCEPT_LANGUAGE_STR() \n | |||||
573 | * NUTAG_ALLOW(), SIPTAG_ALLOW(), and SIPTAG_ALLOW_STR() \n | |||||
574 | * NUTAG_EARLY_MEDIA(), SIPTAG_REQUIRE(), and SIPTAG_REQUIRE_STR() \n | |||||
575 | * NUTAG_SUPPORTED(), SIPTAG_SUPPORTED(), and SIPTAG_SUPPORTED_STR() \n | |||||
576 | * SIPTAG_ALLOW_EVENTS(), SIPTAG_ALLOW_EVENTS_STR() \n | |||||
577 | * SIPTAG_PROXY_AUTHORIZATION(), SIPTAG_PROXY_AUTHORIZATION_STR() \n | |||||
578 | * SIPTAG_AUTHORIZATION(), SIPTAG_AUTHORIZATION_STR() \n | |||||
579 | * SIPTAG_REFERRED_BY(), SIPTAG_REFERRED_BY_STR() \n | |||||
580 | * SIPTAG_REPLACES(), SIPTAG_REPLACES_STR() \n | |||||
581 | * NUTAG_SESSION_TIMER(), NUTAG_SESSION_REFRESHER(), | |||||
582 | * SIPTAG_SESSION_EXPIRES(), SIPTAG_SESSION_EXPIRES_STR() \n | |||||
583 | * NUTAG_MIN_SE(), SIPTAG_MIN_SE(), SIPTAG_MIN_SE_STR() \n | |||||
584 | * SIPTAG_SECURITY_CLIENT(), SIPTAG_SECURITY_CLIENT_STR() \n | |||||
585 | * SIPTAG_SECURITY_VERIFY(), SIPTAG_SECURITY_VERIFY_STR() \n | |||||
586 | * SIPTAG_PRIVACY(), SIPTAG_PRIVACY_STR() \n | |||||
587 | * SIPTAG_MIME_VERSION(), SIPTAG_MIME_VERSION_STR() \n | |||||
588 | * SIPTAG_CONTENT_TYPE(), SIPTAG_CONTENT_TYPE_STR() \n | |||||
589 | * SIPTAG_CONTENT_ENCODING(), SIPTAG_CONTENT_ENCODING_STR() \n | |||||
590 | * SIPTAG_CONTENT_LANGUAGE(), SIPTAG_CONTENT_LANGUAGE_STR() \n | |||||
591 | * SIPTAG_CONTENT_DISPOSITION(), SIPTAG_CONTENT_DISPOSITION_STR() \n | |||||
592 | * SIPTAG_HEADER(), SIPTAG_HEADER_STR() \n | |||||
593 | * SIPTAG_PAYLOAD(), SIPTAG_PAYLOAD_STR() \n | |||||
594 | * | |||||
595 | * @par SDP Handling | |||||
596 | * By default the nua_invite() uses an @ref soa_session_t "SOA media | |||||
597 | * session" object to take care of the Offer/Answer exchange. The SOA can | |||||
598 | * be disabled with tag NUTAG_MEDIA_ENABLE(0). | |||||
599 | * | |||||
600 | * @par | |||||
601 | * The SDP description of the | |||||
602 | * @ref soa_session_t "soa media session" is included in the INVITE request | |||||
603 | * as a message body. | |||||
604 | * The SDP in the message body of the 1XX or 2XX response message is | |||||
605 | * interpreted as an answer, given to the @ref soa_session_t "soa media | |||||
606 | * session" object for processing. | |||||
607 | * | |||||
608 | * @bug If the INVITE request already contains a message body, SDP is not | |||||
609 | * added. Also, if the response contains a multipart body, it is not parsed. | |||||
610 | * | |||||
611 | * @par Tags Related to SDP Management and Offer/Answer Model: | |||||
612 | * NUTAG_MEDIA_ENABLE(), \n | |||||
613 | * NUTAG_INCLUDE_EXTRA_SDP(), \n | |||||
614 | * SOATAG_HOLD(), SOATAG_AF(), SOATAG_ADDRESS(), | |||||
615 | * SOATAG_ORDERED_USER(), SOATAG_REUSE_REJECTED(), | |||||
616 | * SOATAG_RTP_SELECT(), SOATAG_RTP_SORT(), SOATAG_RTP_MISMATCH(), | |||||
617 | * SOATAG_AUDIO_AUX(), \n | |||||
618 | * SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() \n | |||||
619 | * | |||||
620 | * @par Alternative Call Models | |||||
621 | * In addition to the basic SIP call model described in @RFC3261 and | |||||
622 | * @RFC3264, the early media model described in @RFC3262 is available. The | |||||
623 | * use of 100rel and early media can be use can be forced with | |||||
624 | * NUTAG_EARLY_MEDIA(1). | |||||
625 | * | |||||
626 | * Also, the "precondition" call model described in @RFC3312 is supported at | |||||
627 | * SIP level, that is, the SIP PRACK and UPDATE requests are sent if | |||||
628 | * "precondition" is added to the @Require header in the INVITE request. | |||||
629 | * | |||||
630 | * Optionally | |||||
631 | * - uses early media if NUTAG_EARLY_MEDIA() tag is used with non zero-value | |||||
632 | * - media parameters can be set by SOA tags | |||||
633 | * - nua_invite() can be used to change status of an existing call: | |||||
634 | * - #SOATAG_HOLD tag can be used to list the media that will be put on hold, | |||||
635 | * the value "*" sets all the media beloginging to the session on hold | |||||
636 | * | |||||
637 | * @par Authentication | |||||
638 | * The INVITE request may need authentication. Each proxy or server | |||||
639 | * requiring authentication can respond with 401 or 407 response. The | |||||
640 | * nua_authenticate() operation stores authentication information (username | |||||
641 | * and password) to the handle, and stack tries to authenticate all the rest | |||||
642 | * of the requests (e.g., PRACK, ACK, UPDATE, re-INVITE, BYE) using the | |||||
643 | * stored username and password. | |||||
644 | * | |||||
645 | * @sa @ref nua_call_model, #nua_r_invite, #nua_i_state, \n | |||||
646 | * nua_handle_has_active_call() \n | |||||
647 | * nua_handle_has_call_on_hold()\n | |||||
648 | * nua_handle_has_invite() \n | |||||
649 | * nua_authenticate() \n | |||||
650 | * nua_prack() \n | |||||
651 | * nua_update() \n | |||||
652 | * nua_info() \n | |||||
653 | * nua_cancel() \n | |||||
654 | * nua_bye() \n | |||||
655 | * #nua_i_invite, nua_respond() | |||||
656 | */ | |||||
657 | ||||||
658 | /* Tags not implemented | |||||
659 | * NUTAG_REFER_PAUSE() \n | |||||
660 | */ | |||||
661 | ||||||
662 | static int nua_invite_client_init(nua_client_request_t *cr, | |||||
663 | msg_t *msg, sip_t *sip, | |||||
664 | tagi_t const *tags); | |||||
665 | static int nua_invite_client_request(nua_client_request_t *cr, | |||||
666 | msg_t *msg, sip_t *sip, | |||||
667 | tagi_t const *tags); | |||||
668 | static int nua_invite_client_preliminary(nua_client_request_t *cr, | |||||
669 | int status, char const *phrase, | |||||
670 | sip_t const *sip); | |||||
671 | static int nua_invite_client_response(nua_client_request_t *cr, | |||||
672 | int status, char const *phrase, | |||||
673 | sip_t const *sip); | |||||
674 | static int nua_session_client_response(nua_client_request_t *cr, | |||||
675 | int status, char const *phrase, | |||||
676 | sip_t const *sip); | |||||
677 | static int nua_invite_client_report(nua_client_request_t *cr, | |||||
678 | int status, char const *phrase, | |||||
679 | sip_t const *sip, | |||||
680 | nta_outgoing_t *orq, | |||||
681 | tagi_t const *tags); | |||||
682 | ||||||
683 | nua_client_methods_t const nua_invite_client_methods = { | |||||
684 | SIP_METHOD_INVITEsip_method_invite, "INVITE", /* crm_method, crm_method_name */ | |||||
685 | 0, /* crm_extra */ | |||||
686 | { /* crm_flags */ | |||||
687 | /* create_dialog */ 1, | |||||
688 | /* in_dialog */ 1, | |||||
689 | /* target refresh */ 1 | |||||
690 | }, | |||||
691 | NULL((void*)0), /* crm_template */ | |||||
692 | nua_invite_client_init, /* crm_init */ | |||||
693 | nua_invite_client_request, /* crm_send */ | |||||
694 | session_timer_check_restart, /* crm_check_restart */ | |||||
695 | nua_invite_client_response, /* crm_recv */ | |||||
696 | nua_invite_client_preliminary, /* crm_preliminary */ | |||||
697 | nua_invite_client_report, /* crm_report */ | |||||
698 | nua_invite_client_complete, /* crm_complete */ | |||||
699 | }; | |||||
700 | ||||||
701 | extern nua_client_methods_t const nua_bye_client_methods; | |||||
702 | extern nua_client_methods_t const nua_cancel_client_methods; | |||||
703 | extern nua_client_methods_t const nua_update_client_methods; | |||||
704 | extern nua_client_methods_t const nua_prack_client_methods; | |||||
705 | ||||||
706 | int nua_stack_invite(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||||
707 | tagi_t const *tags) | |||||
708 | { | |||||
709 | return nua_client_create(nh, e, &nua_invite_client_methods, tags); | |||||
710 | } | |||||
711 | ||||||
712 | static int nua_invite_client_init(nua_client_request_t *cr, | |||||
713 | msg_t *msg, sip_t *sip, | |||||
714 | tagi_t const *tags) | |||||
715 | { | |||||
716 | nua_handle_t *nh = cr->cr_owner; | |||||
717 | nua_dialog_usage_t *du; | |||||
718 | nua_session_usage_t *ss; | |||||
719 | ||||||
720 | cr->cr_usage = du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
721 | /* Errors returned by nua_invite_client_init() | |||||
722 | do not change the session state */ | |||||
723 | cr->cr_neutral = 1; | |||||
724 | ||||||
725 | if (nh_is_special(nh) || | |||||
726 | nua_stack_set_handle_special(nh, nh_has_invite, nua_i_error)) | |||||
727 | return nua_client_return(cr, 900, "Invalid handle for INVITE", msg); | |||||
728 | else if (nh_referral_check(nh, tags) < 0) | |||||
729 | return nua_client_return(cr, 900, "Invalid referral", msg); | |||||
730 | ||||||
731 | if (du) { | |||||
732 | nua_server_request_t *sr; | |||||
733 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||||
734 | /* INVITE in progress? */ | |||||
735 | if (sr->sr_usage == du && sr->sr_method == sip_method_invite && | |||||
736 | nua_server_request_is_pending(sr)) | |||||
737 | return nua_client_return(cr, SIP_491_REQUEST_PENDING491, sip_491_Request_pending, msg); | |||||
738 | cr->cr_initial = 0; | |||||
739 | } | |||||
740 | else { | |||||
741 | du = nua_dialog_usage_add(nh, nh->nh_ds, nua_session_usage, NULL((void*)0)); | |||||
742 | cr->cr_initial = 1; | |||||
743 | } | |||||
744 | ||||||
745 | if (!du) | |||||
746 | return -1; | |||||
747 | ||||||
748 | ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
749 | ||||||
750 | if (ss->ss_state >= nua_callstate_terminating) | |||||
751 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||||
752 | ||||||
753 | if (nua_client_bind(cr, du) < 0) | |||||
754 | return nua_client_return(cr, 900, "INVITE already in progress", msg); | |||||
755 | ||||||
756 | cr->cr_neutral = 0; | |||||
757 | ||||||
758 | session_timer_preferences(ss->ss_timer, | |||||
759 | sip, | |||||
760 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||||
761 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||||
762 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||||
763 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||||
764 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||||
765 | ||||||
766 | return 0; | |||||
767 | } | |||||
768 | ||||||
769 | static int nua_invite_client_request(nua_client_request_t *cr, | |||||
770 | msg_t *msg, sip_t *sip, | |||||
771 | tagi_t const *tags) | |||||
772 | { | |||||
773 | nua_handle_t *nh = cr->cr_owner; | |||||
774 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
775 | nua_session_usage_t *ss; | |||||
776 | int offer_sent = 0, retval; | |||||
777 | sip_time_t invite_timeout; | |||||
778 | ||||||
779 | if (du == NULL((void*)0)) /* Call terminated */ | |||||
780 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||||
781 | ||||||
782 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
783 | ||||||
784 | if (ss->ss_state >= nua_callstate_terminating) | |||||
785 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||||
786 | ||||||
787 | invite_timeout = NH_PGET(nh, invite_timeout)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_invite_timeout ? ((nh)->nh_prefs)->nhp_invite_timeout : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_invite_timeout); | |||||
788 | if (invite_timeout == 0) | |||||
789 | invite_timeout = UINT_MAX(2147483647 *2U +1U); | |||||
790 | /* Send CANCEL if we don't get response within timeout*/ | |||||
791 | /* nua_dialog_usage_set_expires(du, invite_timeout); Xyzzy */ | |||||
792 | nua_dialog_usage_reset_refresh(du); | |||||
793 | ||||||
794 | /* Add session timer headers */ | |||||
795 | if (session_timer_is_supported(ss->ss_timer)) | |||||
796 | session_timer_add_headers(ss->ss_timer, ss->ss_state == nua_callstate_init, | |||||
797 | msg, sip, nh); | |||||
798 | ||||||
799 | ss->ss_100rel = NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media); | |||||
800 | ss->ss_precondition = sip_has_feature(sip->sip_require, "precondition"); | |||||
801 | if (ss->ss_precondition) | |||||
802 | ss->ss_update_needed = ss->ss_100rel = 1; | |||||
803 | ||||||
804 | if (nh->nh_soa) { | |||||
805 | soa_init_offer_answer(nh->nh_soa); | |||||
806 | ||||||
807 | if (sip->sip_payload) | |||||
808 | offer_sent = 0; /* XXX - kludge */ | |||||
809 | else if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0) | |||||
810 | return -1; | |||||
811 | else | |||||
812 | offer_sent = 1; | |||||
813 | ||||||
814 | if (offer_sent > 0 && | |||||
815 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||||
816 | return nua_client_return(cr, 900, "Internal media error", msg); | |||||
817 | ||||||
818 | if (NH_PGET(nh, media_features)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_media_features ? ((nh)->nh_prefs)->nhp_media_features : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_media_features) && | |||||
819 | !nua_dialog_is_established(nh->nh_ds) && | |||||
820 | !sip->sip_accept_contact && !sip->sip_reject_contact) { | |||||
821 | sip_accept_contact_t ac[1]; | |||||
822 | sip_accept_contact_init(ac); | |||||
823 | ||||||
824 | ac->cp_params = (msg_param_t *) | |||||
825 | soa_media_features(nh->nh_soa, 1, msg_home(msg)((su_home_t*)(msg))); | |||||
826 | ||||||
827 | if (ac->cp_params) { | |||||
828 | msg_header_replace_param(msg_home(msg)((su_home_t*)(msg)), ac->cp_common, "explicit"); | |||||
829 | sip_add_dup(msg, sip, (sip_header_t *)ac); | |||||
830 | } | |||||
831 | } | |||||
832 | } | |||||
833 | else { | |||||
834 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||||
835 | } | |||||
836 | ||||||
837 | retval = nua_base_client_trequest(cr, msg, sip, | |||||
838 | NTATAG_REL100(ss->ss_100rel)ntatag_rel100, tag_bool_v((ss->ss_100rel)), | |||||
839 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||||
840 | if (retval == 0) { | |||||
841 | if ((cr->cr_offer_sent = offer_sent)) | |||||
842 | ss->ss_oa_sent = Offer; | |||||
843 | ||||||
844 | if (!cr->cr_restarting) /* Restart logic calls nua_invite_client_report */ | |||||
845 | signal_call_state_change(nh, ss, 0, "INVITE sent", | |||||
846 | nua_callstate_calling); | |||||
847 | } | |||||
848 | ||||||
849 | return retval; | |||||
850 | } | |||||
851 | ||||||
852 | static int nua_invite_client_response(nua_client_request_t *cr, | |||||
853 | int status, char const *phrase, | |||||
854 | sip_t const *sip) | |||||
855 | { | |||||
856 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
857 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
858 | int uas; | |||||
859 | ||||||
860 | if (ss == NULL((void*)0) || sip == NULL((void*)0)) { | |||||
861 | /* Xyzzy */ | |||||
862 | } | |||||
863 | else if (status < 300) { | |||||
864 | du->du_ready = 1; | |||||
865 | ||||||
866 | if (session_timer_is_supported(ss->ss_timer)) | |||||
867 | session_timer_store(ss->ss_timer, sip); | |||||
868 | ||||||
869 | session_timer_set(ss, uas = 0); | |||||
870 | } | |||||
871 | ||||||
872 | return nua_session_client_response(cr, status, phrase, sip); | |||||
873 | } | |||||
874 | ||||||
875 | static int nua_invite_client_preliminary(nua_client_request_t *cr, | |||||
876 | int status, char const *phrase, | |||||
877 | sip_t const *sip) | |||||
878 | { | |||||
879 | nua_handle_t *nh = cr->cr_owner; | |||||
880 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
881 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
882 | ||||||
883 | assert(sip)((sip) ? (void) (0) : __assert_fail ("sip", "nua_session.c", 883 , __PRETTY_FUNCTION__)); | |||||
884 | ||||||
885 | if (ss && sip && sip->sip_rseq) { | |||||
886 | /* Handle 100rel responses */ | |||||
887 | sip_rseq_t *rseq = sip->sip_rseq; | |||||
888 | ||||||
889 | /* Establish early dialog - we should fork here */ | |||||
890 | if (!nua_dialog_is_established(nh->nh_ds)) { | |||||
891 | nta_outgoing_t *tagged; | |||||
892 | ||||||
893 | nua_dialog_uac_route(nh, nh->nh_ds, sip, 1, 1); | |||||
894 | nua_dialog_store_peer_info(nh, nh->nh_ds, sip); | |||||
895 | ||||||
896 | /* Tag the INVITE request */ | |||||
897 | tagged = nta_outgoing_tagged(cr->cr_orq, | |||||
898 | nua_client_orq_response, cr, | |||||
899 | sip->sip_to->a_tag, sip->sip_rseq); | |||||
900 | if (tagged) { | |||||
901 | nta_outgoing_destroy(cr->cr_orq), cr->cr_orq = tagged; | |||||
902 | } | |||||
903 | else { | |||||
904 | cr->cr_graceful = 1; | |||||
905 | ss->ss_reason = "SIP;cause=500;text=\"Cannot Create Early Dialog\""; | |||||
906 | } | |||||
907 | } | |||||
908 | ||||||
909 | if (!rseq) { | |||||
910 | SU_DEBUG_5(("nua(%p): 100rel missing RSeq\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 910, "nua(%p): 100rel missing RSeq\n" , (void *)nh)) : (void)0); | |||||
911 | } | |||||
912 | else if (nta_outgoing_rseq(cr->cr_orq) > rseq->rs_response) { | |||||
913 | SU_DEBUG_5(("nua(%p): 100rel bad RSeq %u (got %u)\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 915, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0) | |||||
914 | (unsigned)rseq->rs_response,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 915, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0) | |||||
915 | nta_outgoing_rseq(cr->cr_orq)))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 915, "nua(%p): 100rel bad RSeq %u (got %u)\n" , (void *)nh, (unsigned)rseq->rs_response, nta_outgoing_rseq (cr->cr_orq))) : (void)0); | |||||
916 | return 1; /* Do not send event */ | |||||
917 | } | |||||
918 | else if (nta_outgoing_setrseq(cr->cr_orq, rseq->rs_response) < 0) { | |||||
919 | SU_DEBUG_1(("nua(%p): cannot set RSeq %u\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 920, "nua(%p): cannot set RSeq %u\n" , (void *)nh, (unsigned)rseq->rs_response)) : (void)0) | |||||
920 | (unsigned)rseq->rs_response))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 920, "nua(%p): cannot set RSeq %u\n" , (void *)nh, (unsigned)rseq->rs_response)) : (void)0); | |||||
921 | cr->cr_graceful = 1; | |||||
922 | ss->ss_reason = "SIP;cause=400;text=\"Bad RSeq\""; | |||||
923 | } | |||||
924 | } | |||||
925 | ||||||
926 | return nua_session_client_response(cr, status, phrase, sip); | |||||
927 | } | |||||
928 | ||||||
929 | /** Process response to a session request (INVITE, PRACK, UPDATE) */ | |||||
930 | static int nua_session_client_response(nua_client_request_t *cr, | |||||
931 | int status, char const *phrase, | |||||
932 | sip_t const *sip) | |||||
933 | { | |||||
934 | nua_handle_t *nh = cr->cr_owner; | |||||
935 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
936 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
937 | ||||||
938 | char const *sdp = NULL((void*)0); | |||||
939 | size_t len; | |||||
940 | char const *received = NULL((void*)0); | |||||
941 | ||||||
942 | #define LOG3(m)((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 942, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase)) : (void)0) \ | |||||
943 | SU_DEBUG_3(("nua(%p): %s: %s %s in %u %s\n", \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 945, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase)) : (void)0) | |||||
944 | (void *)nh, cr->cr_method_name, (m), \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 945, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase)) : (void)0) | |||||
945 | received ? received : "SDP", status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 945, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received ? received : "SDP", status, phrase)) : (void)0) | |||||
946 | #define LOG5(m)((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 946, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase )) : (void)0) \ | |||||
947 | SU_DEBUG_5(("nua(%p): %s: %s %s in %u %s\n", \((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 948, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase )) : (void)0) | |||||
948 | (void *)nh, cr->cr_method_name, (m), received, status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 948, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, (m), received, status, phrase )) : (void)0) | |||||
949 | ||||||
950 | retry: | |||||
951 | ||||||
952 | if (!ss || !sip || 300 <= status) | |||||
953 | /* Xyzzy */; | |||||
954 | else if (!session_get_description(sip, &sdp, &len)) | |||||
955 | /* No SDP */; | |||||
956 | else if (cr->cr_answer_recv) { | |||||
957 | /* Ignore spurious answers after completing O/A */ | |||||
958 | //LOG3("ignoring duplicate"); | |||||
959 | //sdp = NULL; | |||||
960 | // we need to make sure its *actually* a dup, so we can't assume for now. | |||||
961 | cr->cr_answer_recv = 0; | |||||
962 | goto retry; | |||||
963 | } | |||||
964 | else if (cr->cr_offer_sent) { | |||||
965 | /* case 1: answer to our offer */ | |||||
966 | cr->cr_answer_recv = status; | |||||
967 | received = Answer; | |||||
968 | ||||||
969 | if (nh->nh_soa == NULL((void*)0)) | |||||
970 | LOG5("got SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 970, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("got SDP"), received, status , phrase)) : (void)0); | |||||
971 | else if (soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) < 0) { | |||||
972 | LOG3("error parsing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 972, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("error parsing SDP"), received ? received : "SDP", status, phrase)) : (void)0); | |||||
973 | sdp = NULL((void*)0); | |||||
974 | cr->cr_graceful = 1; | |||||
975 | ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\""; | |||||
976 | } | |||||
977 | else if (soa_process_answer(nh->nh_soa, NULL((void*)0)) < 0) { | |||||
978 | LOG5("error processing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 978, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("error processing SDP") , received, status, phrase)) : (void)0); | |||||
979 | /* XXX */ | |||||
980 | sdp = NULL((void*)0); | |||||
981 | } | |||||
982 | else if (soa_activate(nh->nh_soa, NULL((void*)0)) < 0) { | |||||
983 | /* XXX - what about errors? */ | |||||
984 | LOG3("error activating media after")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 984, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("error activating media after" ), received ? received : "SDP", status, phrase)) : (void)0); | |||||
985 | } | |||||
986 | else { | |||||
987 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
988 | LOG5("processed SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 988, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("processed SDP"), received , status, phrase)) : (void)0); | |||||
989 | } | |||||
990 | } | |||||
991 | else if (cr->cr_method != sip_method_invite) { | |||||
992 | /* If non-invite request did not have offer, ignore SDP in response */ | |||||
993 | LOG3("ignoring extra")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 993, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("ignoring extra"), received ? received : "SDP", status, phrase)) : (void)0); | |||||
994 | sdp = NULL((void*)0); | |||||
995 | } | |||||
996 | else { | |||||
997 | /* case 2: new offer */ | |||||
998 | cr->cr_offer_recv = 1, cr->cr_answer_sent = 0; | |||||
999 | received = Offer; | |||||
1000 | ||||||
1001 | if (nh->nh_soa && soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) < 0) { | |||||
1002 | LOG3("error parsing SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1002, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("error parsing SDP"), received ? received : "SDP", status, phrase)) : (void)0); | |||||
1003 | sdp = NULL((void*)0); | |||||
1004 | cr->cr_graceful = 1; | |||||
1005 | ss->ss_reason = "SIP;cause=400;text=\"Malformed Session Description\""; | |||||
1006 | } | |||||
1007 | else | |||||
1008 | LOG5("got SDP")((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 1008, "nua(%p): %s: %s %s in %u %s\n" , (void *)nh, cr->cr_method_name, ("got SDP"), received, status , phrase)) : (void)0); | |||||
1009 | } | |||||
1010 | ||||||
1011 | if (ss && received) | |||||
1012 | ss->ss_oa_recv = received; | |||||
1013 | ||||||
1014 | if (sdp && nh->nh_soa) | |||||
1015 | return nua_base_client_tresponse(cr, status, phrase, sip, | |||||
1016 | NH_REMOTE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_remote_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_remote_audio_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_remote_video_active(nh-> nh_soa) >= 0) ? tag_skip : soatag_active_video, tag_int_v( soa_is_remote_video_active(nh->nh_soa)), !((1) && ( nh->nh_soa) && soa_is_remote_image_active(nh->nh_soa ) >= 0) ? tag_skip : soatag_active_image, tag_int_v(soa_is_remote_image_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_remote_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_remote_chat_active(nh ->nh_soa)), | |||||
1017 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
1018 | else | |||||
1019 | return nua_base_client_response(cr, status, phrase, sip, NULL((void*)0)); | |||||
1020 | } | |||||
1021 | ||||||
1022 | static int nua_invite_client_report(nua_client_request_t *cr, | |||||
1023 | int status, char const *phrase, | |||||
1024 | sip_t const *sip, | |||||
1025 | nta_outgoing_t *orq, | |||||
1026 | tagi_t const *tags) | |||||
1027 | { | |||||
1028 | nua_handle_t *nh = cr->cr_owner; | |||||
1029 | nua_dialog_state_t *ds = nh->nh_ds; | |||||
1030 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
1031 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
1032 | msg_t *response = nta_outgoing_getresponse(orq); | |||||
1033 | unsigned next_state; | |||||
1034 | int error; | |||||
1035 | ||||||
1036 | nh_referral_respond(nh, status, phrase); /* XXX - restarting after 401/407 */ | |||||
1037 | ||||||
1038 | nua_stack_event(nh->nh_nua, nh, | |||||
1039 | response, | |||||
1040 | (enum nua_event_e)cr->cr_event, | |||||
1041 | status, phrase, | |||||
1042 | tags); | |||||
1043 | ||||||
1044 | if (cr->cr_waiting) | |||||
1045 | /* Do not report call state change if waiting for restart */ | |||||
1046 | return 1; | |||||
1047 | ||||||
1048 | if (ss == NULL((void*)0)) { | |||||
1049 | signal_call_state_change(nh, ss, status, phrase, nua_callstate_terminated); | |||||
1050 | return 1; | |||||
1051 | } | |||||
1052 | ||||||
1053 | ss->ss_reporting = 1; | |||||
1054 | ||||||
1055 | if (cr->cr_neutral) { | |||||
1056 | signal_call_state_change(nh, ss, status, phrase, ss->ss_state); | |||||
1057 | ss->ss_reporting = 0; | |||||
1058 | return 1; | |||||
1059 | } | |||||
1060 | ||||||
1061 | response = msg_ref_create(response); /* Keep reference to contents of sip */ | |||||
1062 | ||||||
1063 | if (orq != cr->cr_orq && cr->cr_orq) { /* Being restarted */ | |||||
1064 | next_state = nua_callstate_calling; | |||||
1065 | } | |||||
1066 | else if (status == 100) { | |||||
1067 | next_state = nua_callstate_calling; | |||||
1068 | } | |||||
1069 | else if (status < 300 && cr->cr_graceful) { | |||||
1070 | next_state = nua_callstate_terminating; | |||||
1071 | if (200 <= status) { | |||||
1072 | nua_invite_client_ack(cr, NULL((void*)0)); | |||||
1073 | } | |||||
1074 | } | |||||
1075 | else if (status < 200) { | |||||
1076 | next_state = nua_callstate_proceeding; | |||||
1077 | ||||||
1078 | if (sip && sip->sip_rseq && | |||||
1079 | !SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_prack)(sip_method_unknown < (sip_method_prack) && (sip_method_prack ) < 32 && ((((nh)->nh_prefs)->nhp_set_.set_bits .nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ( (nh)->nh_nua->nua_handles->nh_prefs)->nhp_appl_method )) && (((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_appl_method))->k_bitmap & (1 << (sip_method_prack))) != 0)) { | |||||
1080 | sip_rack_t rack[1]; | |||||
1081 | ||||||
1082 | sip_rack_init(rack); | |||||
1083 | rack->ra_response = sip->sip_rseq->rs_response; | |||||
1084 | rack->ra_cseq = sip->sip_cseq->cs_seq; | |||||
1085 | rack->ra_method = sip->sip_cseq->cs_method; | |||||
1086 | rack->ra_method_name = sip->sip_cseq->cs_method_name; | |||||
1087 | ||||||
1088 | error = nua_client_tcreate(nh, nua_r_prack, &nua_prack_client_methods, | |||||
1089 | SIPTAG_RACK(rack)siptag_rack, siptag_rack_v(rack), | |||||
1090 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
1091 | if (error < 0) { | |||||
1092 | cr->cr_graceful = 1; | |||||
1093 | next_state = nua_callstate_terminating; | |||||
1094 | } | |||||
1095 | } | |||||
1096 | } | |||||
1097 | else if (status < 300) { | |||||
1098 | next_state = nua_callstate_completing; | |||||
1099 | } | |||||
1100 | else if (cr->cr_terminated) { | |||||
1101 | next_state = nua_callstate_terminated; | |||||
1102 | } | |||||
1103 | else if (cr->cr_graceful && ss->ss_state >= nua_callstate_completing) { | |||||
1104 | next_state = nua_callstate_terminating; | |||||
1105 | } | |||||
1106 | else { | |||||
1107 | next_state = nua_callstate_init; | |||||
1108 | } | |||||
1109 | ||||||
1110 | if (next_state == nua_callstate_calling) { | |||||
1111 | if (sip && sip->sip_status && sip->sip_status->st_status == 100) { | |||||
1112 | ss->ss_reporting = 0; | |||||
1113 | return 1; | |||||
1114 | } | |||||
1115 | } | |||||
1116 | ||||||
1117 | if (next_state == nua_callstate_completing) { | |||||
1118 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||||
1119 | /* Auto-ACK response to re-INVITE when media is enabled | |||||
1120 | and auto_ack is not set to 0 on handle */ | |||||
1121 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||||
1122 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||||
1123 | nua_client_request_t *cru; | |||||
1124 | ||||||
1125 | for (cru = ds->ds_cr; cru; cru = cru->cr_next) { | |||||
1126 | if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv) | |||||
1127 | break; | |||||
1128 | } | |||||
1129 | ||||||
1130 | if (cru) | |||||
1131 | /* A final response to UPDATE or PRACK with answer on its way? */; | |||||
1132 | else if (nua_invite_client_ack(cr, NULL((void*)0)) > 0) | |||||
1133 | next_state = nua_callstate_ready; | |||||
1134 | else | |||||
1135 | next_state = nua_callstate_terminating; | |||||
1136 | } | |||||
1137 | } | |||||
1138 | ||||||
1139 | if (next_state == nua_callstate_terminating) { | |||||
1140 | /* Send BYE or CANCEL */ | |||||
1141 | /* XXX - Forking - send BYE to early dialog?? */ | |||||
1142 | if (ss->ss_state > nua_callstate_proceeding || status >= 200) | |||||
1143 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||||
1144 | else | |||||
1145 | error = nua_client_create(nh, nua_r_cancel, | |||||
1146 | &nua_cancel_client_methods, tags); | |||||
1147 | ||||||
1148 | if (error) { | |||||
1149 | next_state = nua_callstate_terminated; | |||||
1150 | cr->cr_terminated = 1; | |||||
1151 | } | |||||
1152 | cr->cr_graceful = 0; | |||||
1153 | } | |||||
1154 | ||||||
1155 | ss->ss_reporting = 0; | |||||
1156 | ||||||
1157 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||||
1158 | ||||||
1159 | msg_destroy(response); | |||||
1160 | ||||||
1161 | return 1; | |||||
1162 | } | |||||
1163 | ||||||
1164 | /**@fn void nua_ack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
1165 | * | |||||
1166 | * Acknowledge a succesful response to INVITE request. | |||||
1167 | * | |||||
1168 | * Acknowledge a successful response (200..299) to INVITE request with the | |||||
1169 | * SIP ACK request message. This function is needed only if NUTAG_AUTOACK() | |||||
1170 | * parameter has been cleared. | |||||
1171 | * | |||||
1172 | * @param nh Pointer to operation handle | |||||
1173 | * @param tag, value, ... List of tagged parameters | |||||
1174 | * | |||||
1175 | * @return | |||||
1176 | * nothing | |||||
1177 | * | |||||
1178 | * @par Related Tags: | |||||
1179 | * Header tags defined in <sofia-sip/sip_tag.h> | |||||
1180 | * | |||||
1181 | * @par Events: | |||||
1182 | * #nua_i_media_error \n | |||||
1183 | * #nua_i_state (#nua_i_active, #nua_i_terminated) | |||||
1184 | * | |||||
1185 | * @sa NUTAG_AUTOACK(), @ref nua_call_model, #nua_i_state | |||||
1186 | */ | |||||
1187 | ||||||
1188 | int nua_stack_ack(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||||
1189 | tagi_t const *tags) | |||||
1190 | { | |||||
1191 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
1192 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
1193 | nua_client_request_t *cr = du ? du->du_cr : NULL((void*)0); | |||||
1194 | int error; | |||||
1195 | ||||||
1196 | if (!cr || cr->cr_orq == NULL((void*)0) || cr->cr_status < 200) { | |||||
1197 | UA_EVENT2(nua_i_error, 900, "No response to ACK")nua_stack_event(nua, nh, ((void*)0), nua_i_error, 900, "No response to ACK" , ((void*)0)); | |||||
1198 | return 1; | |||||
1199 | } | |||||
1200 | ||||||
1201 | if (tags) | |||||
1202 | nua_stack_set_params(nua, nh, nua_i_error, tags); | |||||
1203 | ||||||
1204 | nua_client_request_ref(cr); | |||||
1205 | error = nua_invite_client_ack(cr, tags); | |||||
1206 | ||||||
1207 | if (error < 0) { | |||||
1208 | if (ss->ss_reason == NULL((void*)0)) | |||||
1209 | ss->ss_reason = "SIP;cause=500;text=\"Internal Error\""; | |||||
1210 | ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ | |||||
1211 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||||
1212 | ss->ss_reporting = 0; | |||||
1213 | signal_call_state_change(nh, ss, 500, "Internal Error", | |||||
1214 | error | |||||
1215 | ? nua_callstate_terminated | |||||
1216 | : nua_callstate_terminating); | |||||
1217 | } | |||||
1218 | else if (ss) | |||||
1219 | signal_call_state_change(nh, ss, 200, "ACK sent", nua_callstate_ready); | |||||
1220 | ||||||
1221 | nua_client_request_unref(cr); | |||||
1222 | ||||||
1223 | return 0; | |||||
1224 | } | |||||
1225 | ||||||
1226 | /** Send ACK, destroy INVITE transaction. | |||||
1227 | * | |||||
1228 | * @retval 1 if successful | |||||
1229 | * @retval < 0 if an error occurred | |||||
1230 | */ | |||||
1231 | static | |||||
1232 | int nua_invite_client_ack(nua_client_request_t *cr, tagi_t const *tags) | |||||
1233 | { | |||||
1234 | nua_handle_t *nh = cr->cr_owner; | |||||
1235 | nua_dialog_state_t *ds = nh->nh_ds; | |||||
1236 | nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage)((cr->cr_usage) ? (void*)((cr->cr_usage) + 1) : ((void* )0)); | |||||
1237 | ||||||
1238 | msg_t *msg; | |||||
1239 | sip_t *sip; | |||||
1240 | int error = -1; | |||||
1241 | sip_authorization_t *wa; | |||||
1242 | sip_proxy_authorization_t *pa; | |||||
1243 | sip_cseq_t *cseq; | |||||
1244 | int proxy_is_set; | |||||
1245 | url_string_t *proxy; | |||||
1246 | nta_outgoing_t *ack; | |||||
1247 | int status = 200; | |||||
1248 | char const *phrase = "OK", *reason = NULL((void*)0); | |||||
1249 | char const *invite_branch; | |||||
1250 | char const *pl_s = NULL((void*)0); | |||||
1251 | ||||||
1252 | assert(cr->cr_orq)((cr->cr_orq) ? (void) (0) : __assert_fail ("cr->cr_orq" , "nua_session.c", 1252, __PRETTY_FUNCTION__)); | |||||
1253 | assert(cr->cr_method == sip_method_invite)((cr->cr_method == sip_method_invite) ? (void) (0) : __assert_fail ("cr->cr_method == sip_method_invite", "nua_session.c", 1253 , __PRETTY_FUNCTION__)); | |||||
1254 | ||||||
1255 | cr->cr_initial = 0; | |||||
1256 | ||||||
1257 | if (!ds->ds_leg) { | |||||
1258 | /* XXX - fix nua_dialog_usage_remove_at() instead! */ | |||||
1259 | goto error; | |||||
1260 | } | |||||
1261 | ||||||
1262 | tl_gets(tags, | |||||
1263 | SIPTAG_PAYLOAD_STR_REF(pl_s)siptag_payload_str_ref, tag_str_vr(&(pl_s)), | |||||
1264 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
1265 | ||||||
1266 | ||||||
1267 | assert(ds->ds_leg)((ds->ds_leg) ? (void) (0) : __assert_fail ("ds->ds_leg" , "nua_session.c", 1267, __PRETTY_FUNCTION__)); | |||||
1268 | ||||||
1269 | msg = nta_outgoing_getrequest(cr->cr_orq); | |||||
1270 | sip = sip_object(msg); | |||||
1271 | if (!msg) | |||||
1272 | goto error; | |||||
1273 | invite_branch = nta_outgoing_branch(cr->cr_orq); | |||||
1274 | ||||||
1275 | wa = sip_authorization(sip)((sip_authorization_t *)msg_header_access((msg_pub_t*)(sip), sip_authorization_class )); | |||||
1276 | pa = sip_proxy_authorization(sip)((sip_proxy_authorization_t *)msg_header_access((msg_pub_t*)( sip), sip_proxy_authorization_class)); | |||||
1277 | ||||||
1278 | msg_destroy(msg); | |||||
1279 | ||||||
1280 | msg = nta_msg_create(nh->nh_nua->nua_nta, 0); | |||||
1281 | sip = sip_object(msg); | |||||
1282 | if (!msg) | |||||
1283 | goto error; | |||||
1284 | ||||||
1285 | cseq = sip_cseq_create(msg_home(msg)((su_home_t*)(msg)), cr->cr_seq, SIP_METHOD_ACKsip_method_ack, "ACK"); | |||||
1286 | ||||||
1287 | if (!cseq) | |||||
1288 | ; | |||||
1289 | else if (nh->nh_tags && sip_add_tl(msg, sip, TAG_NEXT(nh->nh_tags)tag_next, (tag_value_t)(nh->nh_tags)) < 0) | |||||
1290 | ; | |||||
1291 | else if (tags && sip_add_tl(msg, sip, TAG_NEXT(tags)tag_next, (tag_value_t)(tags)) < 0) | |||||
1292 | ; | |||||
1293 | else if (wa && sip_add_dup(msg, sip, (sip_header_t *)wa) < 0) | |||||
1294 | ; | |||||
1295 | else if (pa && sip_add_dup(msg, sip, (sip_header_t *)pa) < 0) | |||||
1296 | ; | |||||
1297 | else if (sip_header_insert(msg, sip, (sip_header_t *)cseq) < 0) | |||||
1298 | ; | |||||
1299 | else if (nta_msg_request_complete(msg, ds->ds_leg, SIP_METHOD_ACKsip_method_ack, "ACK", NULL((void*)0)) < 0) | |||||
1300 | ; | |||||
1301 | else { | |||||
1302 | /* Remove extra headers */ | |||||
1303 | while (sip->sip_allow) | |||||
1304 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_allow); | |||||
1305 | while (sip->sip_priority) | |||||
1306 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_priority); | |||||
1307 | while (sip->sip_proxy_require) | |||||
1308 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_proxy_require); | |||||
1309 | while (sip->sip_require) | |||||
1310 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_require); | |||||
1311 | while (sip->sip_subject) | |||||
1312 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_subject); | |||||
1313 | while (sip->sip_supported) | |||||
1314 | sip_header_remove(msg, sip, (sip_header_t*)sip->sip_supported); | |||||
1315 | ||||||
1316 | if (ss == NULL((void*)0) || ss->ss_state > nua_callstate_ready || pl_s) | |||||
1317 | ; | |||||
1318 | else if (cr->cr_offer_recv && !cr->cr_answer_sent) { | |||||
1319 | if (nh->nh_soa == NULL((void*)0)) { | |||||
1320 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||||
1321 | cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer; | |||||
1322 | } | |||||
1323 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0 || | |||||
1324 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||||
1325 | status = 900, phrase = "Internal media error"; | |||||
1326 | reason = "SIP;cause=500;text=\"Internal media error\""; | |||||
1327 | /* reason = soa_error_as_sip_reason(nh->nh_soa); */ | |||||
1328 | } | |||||
1329 | else { | |||||
1330 | cr->cr_answer_sent = 1, ss->ss_oa_sent = Answer; | |||||
1331 | } | |||||
1332 | } | |||||
1333 | ||||||
1334 | if (ss == NULL((void*)0) || ss->ss_state > nua_callstate_ready || reason) | |||||
1335 | ; | |||||
1336 | else if (nh->nh_soa && soa_is_complete(nh->nh_soa)) { | |||||
1337 | /* signal SOA that O/A round(s) is (are) complete */ | |||||
1338 | if (soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||||
1339 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
1340 | } | |||||
1341 | } | |||||
1342 | else if (nh->nh_soa == NULL((void*)0) | |||||
1343 | /* NUA does not necessarily know dirty details */ | |||||
1344 | /* && !(cr->cr_offer_sent && !cr->cr_answer_recv) */) { | |||||
1345 | ; | |||||
1346 | } | |||||
1347 | else { | |||||
1348 | nua_client_request_t *cru; | |||||
1349 | ||||||
1350 | /* Final response to UPDATE or PRACK may be on its way ... */ | |||||
1351 | for (cru = ds->ds_cr; cru; cru = cru->cr_next) { | |||||
1352 | if (cr != cru && cru->cr_offer_sent && !cru->cr_answer_recv) | |||||
1353 | break; | |||||
1354 | } | |||||
1355 | ||||||
1356 | if (cru == NULL((void*)0)) { | |||||
1357 | /* No SDP answer -> terminate call */ | |||||
1358 | status = 988, phrase = "Incomplete offer/answer"; | |||||
1359 | reason = "SIP;cause=488;text=\"Incomplete offer/answer\""; | |||||
1360 | } | |||||
1361 | } | |||||
1362 | ||||||
1363 | proxy_is_set = NH_PISSET(nh, proxy)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_proxy) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ); | |||||
1364 | proxy = NH_PGET(nh, proxy)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_proxy ? ((nh)-> nh_prefs)->nhp_proxy : ((nh)->nh_nua->nua_handles-> nh_prefs)->nhp_proxy); | |||||
1365 | ||||||
1366 | if ((ack = nta_outgoing_mcreate(nh->nh_nua->nua_nta, NULL((void*)0), NULL((void*)0), NULL((void*)0), | |||||
1367 | msg, | |||||
1368 | NTATAG_ACK_BRANCH(invite_branch)ntatag_ack_branch, tag_str_v((invite_branch)), | |||||
1369 | TAG_IF(proxy_is_set,!(proxy_is_set) ? tag_skip : ntatag_default_proxy, urltag_url_v ((proxy)) | |||||
1370 | NTATAG_DEFAULT_PROXY(proxy))!(proxy_is_set) ? tag_skip : ntatag_default_proxy, urltag_url_v ((proxy)), | |||||
1371 | SIPTAG_END()siptag_end, (tag_value_t)0, | |||||
1372 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)))) { | |||||
1373 | /* TR engine keeps this around for T2 so it catches all 2XX retransmissions */ | |||||
1374 | nta_outgoing_destroy(ack); | |||||
1375 | ||||||
1376 | if (nh->nh_soa && reason && ss && ss->ss_state <= nua_callstate_ready) | |||||
1377 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||||
1378 | nua_i_media_error, status, phrase, | |||||
1379 | NULL((void*)0)); | |||||
1380 | } | |||||
1381 | else if (!reason) { | |||||
1382 | status = 900, phrase = "Cannot send ACK"; | |||||
1383 | reason = "SIP;cause=500;text=\"Internal Error\""; | |||||
1384 | } | |||||
1385 | ||||||
1386 | if (ss && reason) | |||||
1387 | ss->ss_reason = reason; | |||||
1388 | ||||||
1389 | if (status < 300) | |||||
1390 | error = 1; | |||||
1391 | else | |||||
1392 | error = -2; | |||||
1393 | } | |||||
1394 | ||||||
1395 | if (error == -1) | |||||
1396 | msg_destroy(msg); | |||||
1397 | ||||||
1398 | error: | |||||
1399 | cr->cr_acked = 1; /* ... or we have at least tried */ | |||||
1400 | ||||||
1401 | nua_client_request_remove(cr); | |||||
1402 | nua_client_request_clean(cr); | |||||
1403 | ||||||
1404 | return error; | |||||
1405 | } | |||||
1406 | ||||||
1407 | static int | |||||
1408 | nua_invite_client_should_ack(nua_client_request_t const *cr) | |||||
1409 | { | |||||
1410 | return | |||||
1411 | cr && cr->cr_orq && !cr->cr_acked && | |||||
1412 | 200 <= cr->cr_status && cr->cr_status < 300; | |||||
1413 | } | |||||
1414 | ||||||
1415 | /** Complete client request */ | |||||
1416 | static int nua_invite_client_complete(nua_client_request_t *cr) | |||||
1417 | { | |||||
1418 | if (cr->cr_orq == NULL((void*)0)) | |||||
1419 | /* Xyzzy */; | |||||
1420 | else if (cr->cr_status < 200) | |||||
1421 | nta_outgoing_cancel(cr->cr_orq); | |||||
1422 | else if (cr->cr_status < 300 && !cr->cr_acked) | |||||
1423 | nua_invite_client_ack(cr, NULL((void*)0)); | |||||
1424 | ||||||
1425 | return 0; | |||||
1426 | } | |||||
1427 | ||||||
1428 | /**@fn void nua_cancel(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
1429 | * | |||||
1430 | * Cancel an INVITE operation | |||||
1431 | * | |||||
1432 | * @param nh Pointer to operation handle | |||||
1433 | * @param tag, value, ... List of tagged parameters | |||||
1434 | * | |||||
1435 | * @return | |||||
1436 | * nothing | |||||
1437 | * | |||||
1438 | * @par Related Tags: | |||||
1439 | * Header tags defined in <sofia-sip/sip_tag.h> | |||||
1440 | * | |||||
1441 | * @par Events: | |||||
1442 | * #nua_r_cancel, #nua_i_state (#nua_i_active, #nua_i_terminated) | |||||
1443 | * | |||||
1444 | * @sa @ref nua_call_model, nua_invite(), #nua_i_cancel | |||||
1445 | */ | |||||
1446 | ||||||
1447 | static int nua_cancel_client_request(nua_client_request_t *cr, | |||||
1448 | msg_t *msg, sip_t *sip, | |||||
1449 | tagi_t const *tags); | |||||
1450 | static int nua_cancel_client_check_restart(nua_client_request_t *cr, | |||||
1451 | int status, | |||||
1452 | char const *phrase, | |||||
1453 | sip_t const *sip); | |||||
1454 | ||||||
1455 | nua_client_methods_t const nua_cancel_client_methods = { | |||||
1456 | SIP_METHOD_CANCELsip_method_cancel, "CANCEL", /* crm_method, crm_method_name */ | |||||
1457 | 0, /* crm_extra */ | |||||
1458 | { /* crm_flags */ | |||||
1459 | /* create_dialog */ 0, | |||||
1460 | /* in_dialog */ 1, | |||||
1461 | /* target refresh */ 0 | |||||
1462 | }, | |||||
1463 | NULL((void*)0), /* crm_template */ | |||||
1464 | NULL((void*)0), /* crm_init */ | |||||
1465 | nua_cancel_client_request, /* .. not really crm_send */ | |||||
1466 | nua_cancel_client_check_restart, /* crm_check_restart */ | |||||
1467 | NULL((void*)0), /* crm_recv */ | |||||
1468 | NULL((void*)0), /* crm_preliminary */ | |||||
1469 | NULL((void*)0), /* crm_report */ | |||||
1470 | NULL((void*)0), /* crm_complete */ | |||||
1471 | }; | |||||
1472 | ||||||
1473 | int nua_stack_cancel(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||||
1474 | tagi_t const *tags) | |||||
1475 | { | |||||
1476 | return nua_client_create(nh, e, &nua_cancel_client_methods, tags); | |||||
1477 | } | |||||
1478 | ||||||
1479 | static int nua_cancel_client_request(nua_client_request_t *cr, | |||||
1480 | msg_t *msg, sip_t *sip, | |||||
1481 | tagi_t const *tags) | |||||
1482 | { | |||||
1483 | nua_handle_t *nh = cr->cr_owner; | |||||
1484 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
1485 | ||||||
1486 | if (!du || !du->du_cr || !du->du_cr->cr_orq || | |||||
1487 | nta_outgoing_status(du->du_cr->cr_orq) >= 200) { | |||||
1488 | return nua_client_return(cr, 481, "No transaction to CANCEL", msg); | |||||
1489 | } | |||||
1490 | ||||||
1491 | assert(cr->cr_orq == NULL)((cr->cr_orq == ((void*)0)) ? (void) (0) : __assert_fail ( "cr->cr_orq == ((void*)0)", "nua_session.c", 1491, __PRETTY_FUNCTION__ )); | |||||
1492 | ||||||
1493 | cr->cr_orq = nta_outgoing_tcancel(du->du_cr->cr_orq, | |||||
1494 | nua_client_orq_response, | |||||
1495 | nua_client_request_ref(cr), | |||||
1496 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||||
1497 | ||||||
1498 | if (cr->cr_orq == NULL((void*)0)) { | |||||
1499 | nua_client_request_unref(cr); | |||||
1500 | return -1; | |||||
1501 | } | |||||
1502 | ||||||
1503 | return 0; | |||||
1504 | } | |||||
1505 | ||||||
1506 | static int | |||||
1507 | nua_cancel_client_check_restart(nua_client_request_t *cr, | |||||
1508 | int status, | |||||
1509 | char const *phrase, | |||||
1510 | sip_t const *sip) | |||||
1511 | { | |||||
1512 | /* We cannot really restart CANCEL */ | |||||
1513 | return 0; | |||||
1514 | } | |||||
1515 | ||||||
1516 | /** @NUA_EVENT nua_r_cancel | |||||
1517 | * | |||||
1518 | * Answer to outgoing CANCEL. | |||||
1519 | * | |||||
1520 | * The CANCEL may be sent explicitly by nua_cancel() or implicitly by NUA | |||||
1521 | * state machine. | |||||
1522 | * | |||||
1523 | * @param status response status code | |||||
1524 | * @param phrase a short textual description of @a status code | |||||
1525 | * @param nh operation handle associated with the call | |||||
1526 | * @param hmagic application context associated with the call | |||||
1527 | * @param sip response to CANCEL request or NULL upon an error | |||||
1528 | * (status code is in @a status and | |||||
1529 | * descriptive message in @a phrase parameters) | |||||
1530 | * @param tags empty | |||||
1531 | * | |||||
1532 | * @sa nua_cancel(), @ref nua_uac_call_model, #nua_r_invite, nua_invite(), | |||||
1533 | * #nua_i_state | |||||
1534 | * | |||||
1535 | * @END_NUA_EVENT | |||||
1536 | */ | |||||
1537 | ||||||
1538 | static void nua_session_usage_refresh(nua_handle_t *nh, | |||||
1539 | nua_dialog_state_t *ds, | |||||
1540 | nua_dialog_usage_t *du, | |||||
1541 | sip_time_t now) | |||||
1542 | { | |||||
1543 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
1544 | nua_client_request_t const *cr = du->du_cr; | |||||
1545 | nua_server_request_t const *sr; | |||||
1546 | ||||||
1547 | if (ss->ss_state >= nua_callstate_terminating || | |||||
1548 | /* INVITE is in progress or being authenticated */ | |||||
1549 | nua_client_request_in_progress(cr)) | |||||
1550 | return; | |||||
1551 | ||||||
1552 | if (ds->ds_cr) return; /* request queued */ | |||||
1553 | ||||||
1554 | /* UPDATE has been queued */ | |||||
1555 | //for (cr = ds->ds_cr; cr; cr = cr->cr_next) | |||||
1556 | //if (cr->cr_method == sip_method_update) | |||||
1557 | // return; | |||||
1558 | ||||||
1559 | /* INVITE or UPDATE in progress on server side */ | |||||
1560 | for (sr = ds->ds_sr; sr; sr = sr->sr_next) | |||||
1561 | if (sr->sr_usage == du && | |||||
1562 | (sr->sr_method == sip_method_invite || | |||||
1563 | sr->sr_method == sip_method_update)) | |||||
1564 | return; | |||||
1565 | ||||||
1566 | if (ss->ss_timer->refresher == nua_remote_refresher) { | |||||
1567 | SU_DEBUG_3(("nua(%p): session almost expired, sending BYE before timeout.\n", (void *)nh))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1567, "nua(%p): session almost expired, sending BYE before timeout.\n" , (void *)nh)) : (void)0); | |||||
1568 | ss->ss_reason = "SIP;cause=408;text=\"Session timeout\""; | |||||
1569 | nua_stack_bye(nh->nh_nua, nh, nua_r_bye, NULL((void*)0)); | |||||
1570 | return; | |||||
1571 | } | |||||
1572 | else if (NH_PGET(nh, update_refresh)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_update_refresh ? ((nh)->nh_prefs)->nhp_update_refresh : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_update_refresh)) { | |||||
1573 | nua_stack_update(nh->nh_nua, nh, nua_r_update, NULL((void*)0)); | |||||
1574 | } | |||||
1575 | else if (du->du_cr) { | |||||
1576 | nua_client_resend_request(du->du_cr, 0); | |||||
1577 | } | |||||
1578 | else { | |||||
1579 | nua_stack_invite(nh->nh_nua, nh, nua_r_invite, NULL((void*)0)); | |||||
1580 | } | |||||
1581 | } | |||||
1582 | ||||||
1583 | /** @interal Shut down session usage. | |||||
1584 | * | |||||
1585 | * @retval >0 shutdown done | |||||
1586 | * @retval 0 shutdown in progress | |||||
1587 | * @retval <0 try again later | |||||
1588 | */ | |||||
1589 | static int nua_session_usage_shutdown(nua_handle_t *nh, | |||||
1590 | nua_dialog_state_t *ds, | |||||
1591 | nua_dialog_usage_t *du) | |||||
1592 | { | |||||
1593 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
1594 | nua_server_request_t *sr, *sr_next; | |||||
1595 | nua_client_request_t *cri; | |||||
1596 | ||||||
1597 | assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((ss == nua_session_usage_for_dialog(nh->nh_ds)) ? (void) ( 0) : __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 1597, __PRETTY_FUNCTION__)); | |||||
| ||||||
1598 | ||||||
1599 | /* Zap server-side transactions */ | |||||
1600 | for (sr = ds->ds_sr; sr; sr = sr_next) { | |||||
1601 | sr_next = sr->sr_next; | |||||
1602 | if (sr->sr_usage == du) { | |||||
1603 | assert(sr->sr_usage == du)((sr->sr_usage == du) ? (void) (0) : __assert_fail ("sr->sr_usage == du" , "nua_session.c", 1603, __PRETTY_FUNCTION__)); | |||||
1604 | sr->sr_usage = NULL((void*)0); | |||||
1605 | ||||||
1606 | if (nua_server_request_is_pending(sr)) { | |||||
1607 | SR_STATUS1(sr, SIP_480_TEMPORARILY_UNAVAILABLE)sr_status(sr, 480, sip_480_Temporarily_unavailable); | |||||
1608 | nua_server_respond(sr, NULL((void*)0)); | |||||
1609 | if (nua_server_report(sr) >= 2) | |||||
1610 | return 480; | |||||
1611 | } | |||||
1612 | else | |||||
1613 | nua_server_request_destroy(sr); | |||||
1614 | } | |||||
1615 | } | |||||
1616 | ||||||
1617 | cri = du->du_cr; | |||||
| ||||||
1618 | ||||||
1619 | switch (ss->ss_state) { | |||||
1620 | case nua_callstate_calling: | |||||
1621 | case nua_callstate_proceeding: | |||||
1622 | return nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL((void*)0)); | |||||
1623 | ||||||
1624 | case nua_callstate_completing: | |||||
1625 | case nua_callstate_completed: | |||||
1626 | case nua_callstate_ready: | |||||
1627 | if (cri && cri->cr_orq) { | |||||
1628 | if (cri->cr_status < 200) { | |||||
1629 | nua_client_create(nh, nua_r_cancel, &nua_cancel_client_methods, NULL((void*)0)); | |||||
1630 | } | |||||
1631 | else if (cri->cr_status < 300 && !cri->cr_acked) { | |||||
1632 | nua_invite_client_ack(cri, NULL((void*)0)); | |||||
1633 | } | |||||
1634 | } | |||||
1635 | if (nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)) != 0) | |||||
1636 | break; | |||||
1637 | ||||||
1638 | signal_call_state_change(nh, ss, 487, "BYE sent", | |||||
1639 | nua_callstate_terminating); | |||||
1640 | return 0; | |||||
1641 | ||||||
1642 | case nua_callstate_terminating: | |||||
1643 | case nua_callstate_terminated: /* XXX */ | |||||
1644 | return 0; | |||||
1645 | ||||||
1646 | default: | |||||
1647 | break; | |||||
1648 | } | |||||
1649 | ||||||
1650 | nua_dialog_usage_remove(nh, ds, du, NULL((void*)0), NULL((void*)0)); | |||||
1651 | ||||||
1652 | return 200; | |||||
1653 | } | |||||
1654 | ||||||
1655 | /**@fn void nua_prack(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
1656 | * Send a PRACK request. | |||||
1657 | * | |||||
1658 | * PRACK is used to acknowledge receipt of 100rel responses. See @RFC3262. | |||||
1659 | * | |||||
1660 | * @param nh Pointer to operation handle | |||||
1661 | * @param tag, value, ... List of tagged parameters | |||||
1662 | * | |||||
1663 | * @return | |||||
1664 | * nothing | |||||
1665 | * | |||||
1666 | * @par Related Tags: | |||||
1667 | * Tags in <sofia-sip/soa_tag.h>, <sofia-sip/sip_tag.h>. | |||||
1668 | * | |||||
1669 | * @par Events: | |||||
1670 | * #nua_r_prack | |||||
1671 | */ | |||||
1672 | ||||||
1673 | /** @NUA_EVENT nua_r_prack | |||||
1674 | * | |||||
1675 | * Response to an outgoing @b PRACK request. PRACK request is used to | |||||
1676 | * acknowledge reliable preliminary responses and it is usually sent | |||||
1677 | * automatically by the nua stack. | |||||
1678 | * | |||||
1679 | * @param status response status code | |||||
1680 | * (if the request is retried, @a status is 100, the @a | |||||
1681 | * sip->sip_status->st_status contain the real status code | |||||
1682 | * from the response message, e.g., 302, 401, or 407) | |||||
1683 | * @param phrase a short textual description of @a status code | |||||
1684 | * @param nh operation handle associated with the call | |||||
1685 | * @param hmagic application context associated with the call | |||||
1686 | * @param sip response to @b PRACK or NULL upon an error | |||||
1687 | * (status code is in @a status and | |||||
1688 | * descriptive message in @a phrase parameters) | |||||
1689 | * @param tags empty | |||||
1690 | * | |||||
1691 | * @sa nua_prack(), #nua_i_prack, @RFC3262 | |||||
1692 | * | |||||
1693 | * @END_NUA_EVENT | |||||
1694 | */ | |||||
1695 | ||||||
1696 | static int nua_prack_client_init(nua_client_request_t *cr, | |||||
1697 | msg_t *msg, sip_t *sip, | |||||
1698 | tagi_t const *tags); | |||||
1699 | static int nua_prack_client_request(nua_client_request_t *cr, | |||||
1700 | msg_t *msg, sip_t *sip, | |||||
1701 | tagi_t const *tags); | |||||
1702 | static int nua_prack_client_response(nua_client_request_t *cr, | |||||
1703 | int status, char const *phrase, | |||||
1704 | sip_t const *sip); | |||||
1705 | static int nua_prack_client_report(nua_client_request_t *cr, | |||||
1706 | int status, char const *phrase, | |||||
1707 | sip_t const *sip, | |||||
1708 | nta_outgoing_t *orq, | |||||
1709 | tagi_t const *tags); | |||||
1710 | ||||||
1711 | nua_client_methods_t const nua_prack_client_methods = { | |||||
1712 | SIP_METHOD_PRACKsip_method_prack, "PRACK", /* crm_method, crm_method_name */ | |||||
1713 | 0, /* crm_extra */ | |||||
1714 | { /* crm_flags */ | |||||
1715 | /* create_dialog */ 0, | |||||
1716 | /* in_dialog */ 1, | |||||
1717 | /* target refresh */ 0 | |||||
1718 | }, | |||||
1719 | NULL((void*)0), /* crm_template */ | |||||
1720 | nua_prack_client_init, /* crm_init */ | |||||
1721 | nua_prack_client_request, /* crm_send */ | |||||
1722 | NULL((void*)0), /* crm_check_restart */ | |||||
1723 | nua_prack_client_response, /* crm_recv */ | |||||
1724 | NULL((void*)0), /* crm_preliminary */ | |||||
1725 | nua_prack_client_report, /* crm_report */ | |||||
1726 | NULL((void*)0), /* crm_complete */ | |||||
1727 | }; | |||||
1728 | ||||||
1729 | int nua_stack_prack(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||||
1730 | tagi_t const *tags) | |||||
1731 | { | |||||
1732 | return nua_client_create(nh, e, &nua_prack_client_methods, tags); | |||||
1733 | } | |||||
1734 | ||||||
1735 | static int nua_prack_client_init(nua_client_request_t *cr, | |||||
1736 | msg_t *msg, sip_t *sip, | |||||
1737 | tagi_t const *tags) | |||||
1738 | { | |||||
1739 | nua_handle_t *nh = cr->cr_owner; | |||||
1740 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
1741 | ||||||
1742 | cr->cr_usage = du; | |||||
1743 | ||||||
1744 | return 0; | |||||
1745 | } | |||||
1746 | ||||||
1747 | static int nua_prack_client_request(nua_client_request_t *cr, | |||||
1748 | msg_t *msg, sip_t *sip, | |||||
1749 | tagi_t const *tags) | |||||
1750 | { | |||||
1751 | nua_handle_t *nh = cr->cr_owner; | |||||
1752 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
1753 | nua_session_usage_t *ss; | |||||
1754 | nua_client_request_t *cri; | |||||
1755 | int offer_sent = 0, answer_sent = 0, retval; | |||||
1756 | int status = 0; char const *phrase = "PRACK Sent"; | |||||
1757 | //uint32_t rseq = 0; | |||||
1758 | ||||||
1759 | if (du == NULL((void*)0)) /* Call terminated */ | |||||
1760 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||||
1761 | ||||||
1762 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
1763 | if (ss->ss_state >= nua_callstate_terminating) | |||||
1764 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||||
1765 | ||||||
1766 | cri = du->du_cr; | |||||
1767 | ||||||
1768 | // if (sip->sip_rack) | |||||
1769 | // rseq = sip->sip_rack->ra_response; | |||||
1770 | ||||||
1771 | if (cri->cr_offer_recv && !cri->cr_answer_sent) { | |||||
1772 | if (nh->nh_soa == NULL((void*)0)) | |||||
1773 | /* It is up to application to handle SDP */ | |||||
1774 | answer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||||
1775 | else if (sip->sip_payload) | |||||
1776 | /* XXX - we should just do MIME in session_include_description() */; | |||||
1777 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0 || | |||||
1778 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||||
1779 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||||
1780 | SU_DEBUG_3(("nua(%p): local response to PRACK: %d %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1781, "nua(%p): local response to PRACK: %d %s\n" , (void *)nh, status, phrase)) : (void)0) | |||||
1781 | (void *)nh, status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1781, "nua(%p): local response to PRACK: %d %s\n" , (void *)nh, status, phrase)) : (void)0); | |||||
1782 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||||
1783 | nua_i_media_error, status, phrase, | |||||
1784 | NULL((void*)0)); | |||||
1785 | return nua_client_return(cr, status, phrase, msg); | |||||
1786 | } | |||||
1787 | else { | |||||
1788 | answer_sent = 1; | |||||
1789 | if (soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||||
1790 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
1791 | } | |||||
1792 | } | |||||
1793 | } | |||||
1794 | else if (nh->nh_soa == NULL((void*)0)) { | |||||
1795 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||||
1796 | } | |||||
1797 | else { | |||||
1798 | /* When 100rel response status was 183 do support for preconditions */ | |||||
1799 | int send_offer = ss->ss_precondition && | |||||
1800 | cri->cr_status == 183 && cri->cr_offer_sent && cri->cr_answer_recv; | |||||
1801 | ||||||
1802 | if (!send_offer) { | |||||
1803 | tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp); | |||||
1804 | send_offer = t && t->t_value; | |||||
1805 | } | |||||
1806 | ||||||
1807 | if (!send_offer) { | |||||
1808 | } | |||||
1809 | else if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) >= 0 && | |||||
1810 | session_include_description(nh->nh_soa, 1, msg, sip) >= 0) { | |||||
1811 | offer_sent = 1; | |||||
1812 | } | |||||
1813 | else { | |||||
1814 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||||
1815 | SU_DEBUG_3(("nua(%p): PRACK offer: %d %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1816, "nua(%p): PRACK offer: %d %s\n" , (void *)nh, status, phrase)) : (void)0) | |||||
1816 | status, phrase))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 1816, "nua(%p): PRACK offer: %d %s\n" , (void *)nh, status, phrase)) : (void)0); | |||||
1817 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||||
1818 | nua_i_media_error, status, phrase, NULL((void*)0)); | |||||
1819 | return nua_client_return(cr, status, phrase, msg); | |||||
1820 | } | |||||
1821 | } | |||||
1822 | ||||||
1823 | retval = nua_base_client_request(cr, msg, sip, NULL((void*)0)); | |||||
1824 | ||||||
1825 | if (retval == 0) { | |||||
1826 | cr->cr_offer_sent = offer_sent; | |||||
1827 | cr->cr_answer_sent = answer_sent; | |||||
1828 | ||||||
1829 | if (offer_sent) | |||||
1830 | ss->ss_oa_sent = Offer; | |||||
1831 | else if (answer_sent) | |||||
1832 | ss->ss_oa_sent = Answer; | |||||
1833 | ||||||
1834 | if (cr->cr_restarting) | |||||
1835 | /* Restart logic calls nua_prack_client_report */; | |||||
1836 | else if (!cr->cr_auto && (!offer_sent || !answer_sent)) | |||||
1837 | /* Suppose application know it called nua_prack() */; | |||||
1838 | else | |||||
1839 | signal_call_state_change(nh, ss, status, phrase, ss->ss_state); | |||||
1840 | } | |||||
1841 | ||||||
1842 | return retval; | |||||
1843 | } | |||||
1844 | ||||||
1845 | static int nua_prack_client_response(nua_client_request_t *cr, | |||||
1846 | int status, char const *phrase, | |||||
1847 | sip_t const *sip) | |||||
1848 | { | |||||
1849 | /* XXX - fatal error cases? */ | |||||
1850 | ||||||
1851 | return nua_session_client_response(cr, status, phrase, sip); | |||||
1852 | } | |||||
1853 | ||||||
1854 | static int nua_prack_client_report(nua_client_request_t *cr, | |||||
1855 | int status, char const *phrase, | |||||
1856 | sip_t const *sip, | |||||
1857 | nta_outgoing_t *orq, | |||||
1858 | tagi_t const *tags) | |||||
1859 | { | |||||
1860 | nua_handle_t *nh = cr->cr_owner; | |||||
1861 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
1862 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
1863 | int acked = 0; | |||||
1864 | ||||||
1865 | nua_stack_event(nh->nh_nua, nh, | |||||
1866 | nta_outgoing_getresponse(orq), | |||||
1867 | (enum nua_event_e)cr->cr_event, | |||||
1868 | status, phrase, | |||||
1869 | tags); | |||||
1870 | ||||||
1871 | if (!ss || cr->cr_terminated || cr->cr_graceful || cr->cr_waiting) | |||||
1872 | return 1; | |||||
1873 | ||||||
1874 | if (cr->cr_offer_sent || cr->cr_answer_sent) { | |||||
1875 | unsigned next_state = ss->ss_state; | |||||
1876 | ||||||
1877 | if (status < 200) | |||||
1878 | ; | |||||
1879 | else if (nua_invite_client_should_ack(du->du_cr)) { | |||||
1880 | /* There is an un-ACK-ed INVITE there */ | |||||
1881 | assert(du->du_cr->cr_method == sip_method_invite)((du->du_cr->cr_method == sip_method_invite) ? (void) ( 0) : __assert_fail ("du->du_cr->cr_method == sip_method_invite" , "nua_session.c", 1881, __PRETTY_FUNCTION__)); | |||||
1882 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||||
1883 | /* Auto-ACK response to re-INVITE when media is enabled | |||||
1884 | and auto_ack is not set to 0 on handle */ | |||||
1885 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||||
1886 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||||
1887 | /* There should be no UPDATE with offer/answer | |||||
1888 | if PRACK with offer/answer was ongoing! */ | |||||
1889 | if (nua_invite_client_ack(du->du_cr, NULL((void*)0)) > 0) | |||||
1890 | next_state = nua_callstate_ready; | |||||
1891 | else | |||||
1892 | next_state = nua_callstate_terminating; | |||||
1893 | ||||||
1894 | acked = 1; | |||||
1895 | } | |||||
1896 | } | |||||
1897 | ||||||
1898 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||||
1899 | } | |||||
1900 | ||||||
1901 | if (acked && | |||||
1902 | nua_client_is_queued(du->du_cr) && | |||||
1903 | du->du_cr->cr_method == sip_method_invite) { | |||||
1904 | /* New INVITE was queued - do not send UPDATE */ | |||||
1905 | } | |||||
1906 | else if (ss->ss_update_needed && 200 <= status && status < 300 && | |||||
1907 | !SIP_IS_ALLOWED(NH_PGET(nh, appl_method), sip_method_update)(sip_method_unknown < (sip_method_update) && (sip_method_update ) < 32 && ((((nh)->nh_prefs)->nhp_set_.set_bits .nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ( (nh)->nh_nua->nua_handles->nh_prefs)->nhp_appl_method )) && (((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_appl_method ? ((nh)->nh_prefs)->nhp_appl_method : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_appl_method))->k_bitmap & (1 << (sip_method_update))) != 0)) | |||||
1908 | nua_client_create(nh, nua_r_update, &nua_update_client_methods, NULL((void*)0)); | |||||
1909 | ||||||
1910 | return 1; | |||||
1911 | } | |||||
1912 | ||||||
1913 | /* ---------------------------------------------------------------------- */ | |||||
1914 | /* UAS side of INVITE */ | |||||
1915 | ||||||
1916 | /** @NUA_EVENT nua_i_invite | |||||
1917 | * | |||||
1918 | * Indication of incoming call or re-INVITE request. | |||||
1919 | * | |||||
1920 | * @param status statuscode of response sent automatically by stack | |||||
1921 | * @param phrase a short textual description of @a status code | |||||
1922 | * @param nh operation handle associated with this call | |||||
1923 | * (maybe created for this call) | |||||
1924 | * @param hmagic application context associated with this call | |||||
1925 | * (maybe NULL if call handle was created for this call) | |||||
1926 | * @param sip incoming INVITE request | |||||
1927 | * @param tags SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO() | |||||
1928 | * | |||||
1929 | * @par | |||||
1930 | * @par Responding to INVITE with nua_respond() | |||||
1931 | * | |||||
1932 | * If @a status in #nua_i_invite event is below 200, the application should | |||||
1933 | * accept or reject the call with nua_respond(). See the @ref nua_call_model | |||||
1934 | * for the detailed explanation of various options in call processing at | |||||
1935 | * server end. | |||||
1936 | * | |||||
1937 | * The @b INVITE request takes care of session setup using SDP Offer-Answer | |||||
1938 | * negotiation as specified in @RFC3264 (updated in @RFC3262 section 5, | |||||
1939 | * @RFC3311, and @RFC3312). The Offer-Answer can be taken care by | |||||
1940 | * application (if NUTAG_MEDIA_ENABLE(0) parameter has been set) or by the | |||||
1941 | * built-in SDP Offer/Answer engine @soa (by default and when | |||||
1942 | * NUTAG_MEDIA_ENABLE(1) parameter has been set). When @soa is enabled, it | |||||
1943 | * will take care of parsing the SDP, negotiating the media and codecs, and | |||||
1944 | * including the SDP in the SIP message bodies as required by the | |||||
1945 | * Offer-Answer model. | |||||
1946 | * | |||||
1947 | * When @soa is enabled, the SDP in the incoming INVITE is parsed and feed | |||||
1948 | * to a #soa_session_t object. The #nua_i_state event sent to the | |||||
1949 | * application immediately after #nua_i_invite will contain the parsing | |||||
1950 | * results in SOATAG_REMOTE_SDP() and SOATAG_REMOTE_SDP_STR() tags. | |||||
1951 | * | |||||
1952 | * Note that currently the parser within @nua does not handle MIME | |||||
1953 | * multipart. The SDP Offer/Answer engine can get confused if the SDP offer | |||||
1954 | * is included in a MIME multipart, therefore such an @b INVITE is rejected | |||||
1955 | * with <i>415 Unsupported Media Type</i> error response: the client is | |||||
1956 | * expected to retry the INVITE without MIME multipart content. | |||||
1957 | * | |||||
1958 | * If the call is to be accepted, the application should include the SDP in | |||||
1959 | * the 2XX response. If @soa is not disabled with NUTAG_MEDIA_ENABLE(0), the | |||||
1960 | * SDP should be included in the SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() | |||||
1961 | * parameter given to nua_respond(). If it is disabled, the SDP should be | |||||
1962 | * included in the response message using SIPTAG_PAYLOAD() or | |||||
1963 | * SIPTAG_PAYLOAD_STR(). Also, the @ContentType should be set using | |||||
1964 | * SIPTAG_CONTENT_TYPE() or SIPTAG_CONTENT_TYPE_STR(). | |||||
1965 | * | |||||
1966 | * @par Preliminary Responses and 100rel | |||||
1967 | * | |||||
1968 | * Call progress can be signaled with preliminary responses (with status | |||||
1969 | * code in the range 101..199). It is possible to conclude the SDP | |||||
1970 | * Offer-Answer negotiation using preliminary responses, too. If | |||||
1971 | * NUTAG_EARLY_ANSWER(1), SOATAG_USER_SDP() or SOATAG_USER_SDP_STR() | |||||
1972 | * parameter is included with in a preliminary nua_response(), the SDP | |||||
1973 | * answer is generated and sent with the preliminary responses, too. | |||||
1974 | * | |||||
1975 | * The preliminary responses are sent reliably if feature tag "100rel" is | |||||
1976 | * included in the @Require header of the response or if | |||||
1977 | * NUTAG_EARLY_MEDIA(1) parameter has been given. The reliably delivery of | |||||
1978 | * preliminary responses mean that a sequence number is included in the | |||||
1979 | * @RSeq header in the response message and the response message is resent | |||||
1980 | * until the client responds with a @b PRACK request with matching sequence | |||||
1981 | * number in @RAck header. | |||||
1982 | * | |||||
1983 | * Note that only the "183" response is sent reliably if the | |||||
1984 | * NUTAG_ONLY183_100REL(1) parameter has been given. The reliable | |||||
1985 | * preliminary responses are acknowledged with @b PRACK request sent by the | |||||
1986 | * client. | |||||
1987 | * | |||||
1988 | * Note if the SDP offer-answer is completed with the reliable preliminary | |||||
1989 | * responses, the is no need to include SDP in 200 OK response (or other 2XX | |||||
1990 | * response). However, it the tag NUTAG_INCLUDE_EXTRA_SDP(1) is included | |||||
1991 | * with nua_respond(), a copy of the SDP answer generated earlier by @soa is | |||||
1992 | * included as the message body. | |||||
1993 | * | |||||
1994 | * @sa nua_respond(), @ref nua_uas_call_model, #nua_i_state, | |||||
1995 | * NUTAG_MEDIA_ENABLE(), SOATAG_USER_SDP(), SOATAG_USER_SDP_STR(), | |||||
1996 | * @RFC3262, NUTAG_EARLY_ANSWER(), NUTAG_EARLY_MEDIA(), | |||||
1997 | * NUTAG_ONLY183_100REL(), | |||||
1998 | * NUTAG_INCLUDE_EXTRA_SDP(), | |||||
1999 | * #nua_i_prack, #nua_i_update, nua_update(), | |||||
2000 | * nua_invite(), #nua_r_invite | |||||
2001 | * | |||||
2002 | * @par | |||||
2003 | * @par Third Party Call Control | |||||
2004 | * | |||||
2005 | * When so called 2rd party call control is used, the initial @b INVITE may | |||||
2006 | * not contain SDP offer. In that case, the offer is sent by the recipient | |||||
2007 | * of the @b INVITE request (User-Agent Server, UAS). The SDP sent in 2XX | |||||
2008 | * response (or in a preliminary reliable response) is considered as an | |||||
2009 | * offer, and the answer will be included in the @b ACK request sent by the | |||||
2010 | * UAC (or @b PRACK in case of preliminary reliable response). | |||||
2011 | * | |||||
2012 | * @sa @ref nua_3pcc_call_model | |||||
2013 | * | |||||
2014 | * @END_NUA_EVENT | |||||
2015 | */ | |||||
2016 | ||||||
2017 | static int nua_invite_server_init(nua_server_request_t *sr); | |||||
2018 | static int nua_session_server_init(nua_server_request_t *sr); | |||||
2019 | static int nua_invite_server_preprocess(nua_server_request_t *sr); | |||||
2020 | static int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *); | |||||
2021 | static int nua_invite_server_is_100rel(nua_server_request_t *, tagi_t const *); | |||||
2022 | static int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *); | |||||
2023 | ||||||
2024 | static int | |||||
2025 | process_ack_or_cancel(nua_server_request_t *, nta_incoming_t *, | |||||
2026 | sip_t const *), | |||||
2027 | process_ack(nua_server_request_t *, nta_incoming_t *, sip_t const *), | |||||
2028 | process_ack_error(nua_server_request_t *sr, msg_t *ackmsg, | |||||
2029 | int status, char const *phrase, char const *reason), | |||||
2030 | process_cancel(nua_server_request_t *, nta_incoming_t *, sip_t const *), | |||||
2031 | process_timeout(nua_server_request_t *, nta_incoming_t *), | |||||
2032 | process_prack(nua_server_request_t *, | |||||
2033 | nta_reliable_t *rel, | |||||
2034 | nta_incoming_t *irq, | |||||
2035 | sip_t const *sip); | |||||
2036 | ||||||
2037 | nua_server_methods_t const nua_invite_server_methods = | |||||
2038 | { | |||||
2039 | SIP_METHOD_INVITEsip_method_invite, "INVITE", | |||||
2040 | nua_i_invite, /* Event */ | |||||
2041 | { | |||||
2042 | 1, /* Create dialog */ | |||||
2043 | 0, /* Initial request */ | |||||
2044 | 1, /* Target refresh request */ | |||||
2045 | 1, /* Add Contact */ | |||||
2046 | }, | |||||
2047 | nua_invite_server_init, | |||||
2048 | nua_invite_server_preprocess, | |||||
2049 | nua_base_server_params((void*)0), | |||||
2050 | nua_invite_server_respond, | |||||
2051 | nua_invite_server_report, | |||||
2052 | }; | |||||
2053 | ||||||
2054 | ||||||
2055 | /** @internal Preprocess incoming invite - sure we have a valid request. | |||||
2056 | * | |||||
2057 | * @return 0 if request is valid, or error statuscode otherwise | |||||
2058 | */ | |||||
2059 | static int | |||||
2060 | nua_invite_server_init(nua_server_request_t *sr) | |||||
2061 | { | |||||
2062 | nua_handle_t *nh = sr->sr_owner; | |||||
2063 | nua_t *nua = nh->nh_nua; | |||||
2064 | nua_session_usage_t *ss; | |||||
2065 | ||||||
2066 | sr->sr_neutral = 1; | |||||
2067 | ||||||
2068 | if (!NUA_PGET(nua, nh, invite_enable)(((nh) ? (nh)->nh_prefs : (nua)->nua_handles->nh_prefs )->nhp_set_.set_bits.nhb_invite_enable ? ((nh) ? (nh)-> nh_prefs : (nua)->nua_handles->nh_prefs)->nhp_invite_enable : ((nua)->nua_handles->nh_prefs)->nhp_invite_enable )) | |||||
2069 | return SR_STATUS1(sr, SIP_403_FORBIDDEN)sr_status(sr, 403, sip_403_Forbidden); | |||||
2070 | ||||||
2071 | if (nua_session_server_init(sr)) | |||||
2072 | return sr->sr_status; | |||||
2073 | ||||||
2074 | if (sr->sr_usage) { | |||||
2075 | /* Existing session - check for overlap and glare */ | |||||
2076 | ||||||
2077 | nua_server_request_t const *sr0; | |||||
2078 | nua_client_request_t const *cr; | |||||
2079 | ||||||
2080 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) { | |||||
2081 | /* Previous INVITE has not been ACKed */ | |||||
2082 | if (sr0->sr_method == sip_method_invite) | |||||
2083 | break; | |||||
2084 | /* Or we have sent offer but have not received an answer */ | |||||
2085 | if (sr->sr_sdp && sr0->sr_offer_sent && !sr0->sr_answer_recv) | |||||
2086 | break; | |||||
2087 | /* Or we have received request with offer but not sent an answer */ | |||||
2088 | if (sr->sr_sdp && sr0->sr_offer_recv && !sr0->sr_answer_sent) | |||||
2089 | break; | |||||
2090 | } | |||||
2091 | ||||||
2092 | if (sr0) { | |||||
2093 | /* Overlapping invites - RFC 3261 14.2 */ | |||||
2094 | return nua_server_retry_after(sr, 500, "Overlapping Requests", 0, 10); | |||||
2095 | } | |||||
2096 | ||||||
2097 | for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) { | |||||
2098 | if (cr->cr_usage == sr->sr_usage && cr->cr_orq && cr->cr_offer_sent) | |||||
2099 | /* Glare - RFC 3261 14.2 and RFC 3311 section 5.2 */ | |||||
2100 | return SR_STATUS1(sr, SIP_491_REQUEST_PENDING)sr_status(sr, 491, sip_491_Request_pending); | |||||
2101 | } | |||||
2102 | ||||||
2103 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2104 | ||||||
2105 | if (ss->ss_state < nua_callstate_ready && | |||||
2106 | ss->ss_state != nua_callstate_init) { | |||||
2107 | return nua_server_retry_after(sr, 500, "Overlapping Requests 2", 0, 10); | |||||
2108 | } | |||||
2109 | } | |||||
2110 | ||||||
2111 | sr->sr_neutral = 0; | |||||
2112 | ||||||
2113 | return 0; | |||||
2114 | } | |||||
2115 | ||||||
2116 | /** Initialize session server request. | |||||
2117 | * | |||||
2118 | * Ensure that the request is valid. | |||||
2119 | */ | |||||
2120 | static int | |||||
2121 | nua_session_server_init(nua_server_request_t *sr) | |||||
2122 | { | |||||
2123 | nua_handle_t *nh = sr->sr_owner; | |||||
2124 | nua_t *nua = nh->nh_nua; | |||||
2125 | ||||||
2126 | msg_t *msg = sr->sr_response.msg; | |||||
2127 | sip_t *sip = sr->sr_response.sip; | |||||
2128 | ||||||
2129 | sip_t *request = (sip_t *) sr->sr_request.sip; | |||||
2130 | ||||||
2131 | if (!sr->sr_initial) | |||||
2132 | sr->sr_usage = nua_dialog_usage_get(nh->nh_ds, nua_session_usage, NULL((void*)0)); | |||||
2133 | ||||||
2134 | if (sr->sr_method != sip_method_invite && sr->sr_usage == NULL((void*)0)) { | |||||
2135 | /* UPDATE/PRACK sent within an existing dialog? */ | |||||
2136 | return SR_STATUS(sr, 481, "Call Does Not Exist")((sr)->sr_phrase = ("Call Does Not Exist"), (sr)->sr_status = (481)); | |||||
2137 | } | |||||
2138 | else if (sr->sr_usage) { | |||||
2139 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2140 | if (ss->ss_state >= nua_callstate_terminating) | |||||
2141 | return SR_STATUS(sr, 481, "Call is being terminated")((sr)->sr_phrase = ("Call is being terminated"), (sr)-> sr_status = (481)); | |||||
2142 | } | |||||
2143 | ||||||
2144 | if (nh->nh_soa) { | |||||
2145 | sip_accept_t *a = nua->nua_invite_accept; | |||||
2146 | ||||||
2147 | /* XXX - soa should know what it supports */ | |||||
2148 | sip_add_dup(msg, sip, (sip_header_t *)a); | |||||
2149 | ||||||
2150 | /* if we see there is a multipart content-type, | |||||
2151 | parse it into the sip structre and find the SDP and replace it | |||||
2152 | into the request as the requested content */ | |||||
2153 | if (request->sip_content_type && | |||||
2154 | su_casenmatch(request->sip_content_type->c_type, "multipart/", 10)) { | |||||
2155 | msg_multipart_t *mp, *mpp; | |||||
2156 | ||||||
2157 | if (request->sip_multipart) { | |||||
2158 | mp = request->sip_multipart; | |||||
2159 | } else { | |||||
2160 | mp = msg_multipart_parse(nua_handle_home(nh)((su_home_t *)(nh)), | |||||
2161 | request->sip_content_type, | |||||
2162 | (sip_payload_t *)request->sip_payload); | |||||
2163 | request->sip_multipart = mp; | |||||
2164 | } | |||||
2165 | ||||||
2166 | if (mp) { | |||||
2167 | int sdp = 0; | |||||
2168 | ||||||
2169 | /* extract the SDP and set the primary content-type and payload to that SDP as if it was the only content so SOA will work */ | |||||
2170 | for(mpp = mp; mpp; mpp = mpp->mp_next) { | |||||
2171 | if (mpp->mp_content_type && mpp->mp_content_type->c_type && | |||||
2172 | mpp->mp_payload && mpp->mp_payload->pl_data && | |||||
2173 | su_casenmatch(mpp->mp_content_type->c_type, "application/sdp", 15)) { | |||||
2174 | ||||||
2175 | request->sip_content_type = msg_content_type_dup(nua_handle_home(nh)((su_home_t *)(nh)), mpp->mp_content_type); | |||||
2176 | ||||||
2177 | if (request->sip_content_length) { | |||||
2178 | request->sip_content_length->l_length = mpp->mp_payload->pl_len; | |||||
2179 | } | |||||
2180 | ||||||
2181 | request->sip_payload->pl_data = su_strdup(nua_handle_home(nh)((su_home_t *)(nh)), mpp->mp_payload->pl_data); | |||||
2182 | request->sip_payload->pl_len = mpp->mp_payload->pl_len; | |||||
2183 | ||||||
2184 | sdp++; | |||||
2185 | ||||||
2186 | break; | |||||
2187 | } | |||||
2188 | } | |||||
2189 | ||||||
2190 | /* insist on the existance of a SDP in the content or refuse the request */ | |||||
2191 | if (!sdp) { | |||||
2192 | return SR_STATUS1(sr, SIP_406_NOT_ACCEPTABLE)sr_status(sr, 406, sip_406_Not_acceptable); | |||||
2193 | } | |||||
2194 | } | |||||
2195 | } | |||||
2196 | ||||||
2197 | ||||||
2198 | /* Make sure caller uses application/sdp without compression */ | |||||
2199 | if (nta_check_session_content(NULL((void*)0), request, a, TAG_END()(tag_type_t)0, (tag_value_t)0)) { | |||||
2200 | sip_add_make(msg, sip, sip_accept_encoding_class, ""); | |||||
2201 | return SR_STATUS1(sr, SIP_415_UNSUPPORTED_MEDIA)sr_status(sr, 415, sip_415_Unsupported_media); | |||||
2202 | } | |||||
2203 | ||||||
2204 | /* Make sure caller accepts application/sdp */ | |||||
2205 | if (nta_check_accept(NULL((void*)0), request, a, NULL((void*)0), TAG_END()(tag_type_t)0, (tag_value_t)0)) { | |||||
2206 | sip_add_make(msg, sip, sip_accept_encoding_class, ""); | |||||
2207 | return SR_STATUS1(sr, SIP_406_NOT_ACCEPTABLE)sr_status(sr, 406, sip_406_Not_acceptable); | |||||
2208 | } | |||||
2209 | } | |||||
2210 | ||||||
2211 | if (request->sip_session_expires && | |||||
2212 | sip_has_feature(NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), "timer") && | |||||
2213 | session_timer_check_min_se(msg, sip, request, NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se))) { | |||||
2214 | if (sip->sip_min_se) | |||||
2215 | return SR_STATUS1(sr, SIP_422_SESSION_TIMER_TOO_SMALL)sr_status(sr, 422, sip_422_Session_timer); | |||||
2216 | else | |||||
2217 | return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||||
2218 | } | |||||
2219 | ||||||
2220 | session_get_description(request, &sr->sr_sdp, &sr->sr_sdp_len); | |||||
2221 | ||||||
2222 | return 0; | |||||
2223 | } | |||||
2224 | ||||||
2225 | /** Preprocess INVITE. | |||||
2226 | * | |||||
2227 | * This is called after a handle has been created for an incoming INVITE. | |||||
2228 | */ | |||||
2229 | int nua_invite_server_preprocess(nua_server_request_t *sr) | |||||
2230 | { | |||||
2231 | nua_handle_t *nh = sr->sr_owner; | |||||
2232 | nua_dialog_state_t *ds = nh->nh_ds; | |||||
2233 | nua_session_usage_t *ss; | |||||
2234 | ||||||
2235 | sip_t const *request = sr->sr_request.sip; | |||||
2236 | ||||||
2237 | assert(sr->sr_status == 100)((sr->sr_status == 100) ? (void) (0) : __assert_fail ("sr->sr_status == 100" , "nua_session.c", 2237, __PRETTY_FUNCTION__)); | |||||
2238 | assert(nh != nh->nh_nua->nua_dhandle)((nh != nh->nh_nua->nua_handles) ? (void) (0) : __assert_fail ("nh != nh->nh_nua->nua_handles", "nua_session.c", 2238 , __PRETTY_FUNCTION__)); | |||||
2239 | ||||||
2240 | if (sr->sr_status > 100) | |||||
2241 | return sr->sr_status; | |||||
2242 | ||||||
2243 | if (nh->nh_soa) | |||||
2244 | soa_init_offer_answer(nh->nh_soa); | |||||
2245 | ||||||
2246 | if (sr->sr_sdp) { | |||||
2247 | if (nh->nh_soa && | |||||
2248 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||||
2249 | SU_DEBUG_5(("nua(%p): %s server: error parsing SDP\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2250, "nua(%p): %s server: error parsing SDP\n" , (void *)nh, "INVITE")) : (void)0) | |||||
2250 | "INVITE"))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2250, "nua(%p): %s server: error parsing SDP\n" , (void *)nh, "INVITE")) : (void)0); | |||||
2251 | return SR_STATUS(sr, 400, "Bad Session Description")((sr)->sr_phrase = ("Bad Session Description"), (sr)->sr_status = (400)); | |||||
2252 | } | |||||
2253 | else | |||||
2254 | sr->sr_offer_recv = 1; | |||||
2255 | } | |||||
2256 | ||||||
2257 | /* Add the session usage */ | |||||
2258 | if (sr->sr_usage == NULL((void*)0)) { | |||||
2259 | sr->sr_usage = nua_dialog_usage_add(nh, ds, nua_session_usage, NULL((void*)0)); | |||||
2260 | if (sr->sr_usage == NULL((void*)0)) | |||||
2261 | return SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||||
2262 | } | |||||
2263 | ||||||
2264 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2265 | ||||||
2266 | if (sr->sr_offer_recv) | |||||
2267 | ss->ss_oa_recv = Offer; | |||||
2268 | ||||||
2269 | ss->ss_100rel = NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media); | |||||
2270 | ss->ss_precondition = sip_has_feature(request->sip_require, "precondition"); | |||||
2271 | if (ss->ss_precondition) | |||||
2272 | ss->ss_100rel = 1; | |||||
2273 | ||||||
2274 | session_timer_store(ss->ss_timer, request); | |||||
2275 | ||||||
2276 | #if 0 /* The glare and overlap tests should take care of this. */ | |||||
2277 | assert(ss->ss_state >= nua_callstate_completed ||((ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init) ? (void) (0) : __assert_fail ("ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init" , "nua_session.c", 2278, __PRETTY_FUNCTION__)) | |||||
2278 | ss->ss_state == nua_callstate_init)((ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init) ? (void) (0) : __assert_fail ("ss->ss_state >= nua_callstate_completed || ss->ss_state == nua_callstate_init" , "nua_session.c", 2278, __PRETTY_FUNCTION__)); | |||||
2279 | #endif | |||||
2280 | ||||||
2281 | if (NH_PGET(nh, auto_answer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_answer ? ( (nh)->nh_prefs)->nhp_auto_answer : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_answer) || | |||||
2282 | /* Auto-answer to re-INVITE unless auto_answer is set to 0 on handle */ | |||||
2283 | (ss->ss_state == nua_callstate_ready && | |||||
2284 | /* Auto-answer requires enabled media (soa). | |||||
2285 | * XXX - if the re-INVITE modifies the media we should not auto-answer. | |||||
2286 | */ | |||||
2287 | nh->nh_soa && | |||||
2288 | !NH_PISSET(nh, auto_answer)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_answer) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||||
2289 | SR_STATUS1(sr, SIP_200_OK)sr_status(sr, 200, sip_200_OK); | |||||
2290 | } | |||||
2291 | else if (NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||||
2292 | if (ss->ss_100rel && | |||||
2293 | (sip_has_feature(request->sip_supported, "100rel") || | |||||
2294 | sip_has_feature(request->sip_require, "100rel"))) { | |||||
2295 | SR_STATUS1(sr, SIP_183_SESSION_PROGRESS)sr_status(sr, 183, sip_183_Session_progress); | |||||
2296 | } | |||||
2297 | else { | |||||
2298 | SR_STATUS1(sr, SIP_180_RINGING)sr_status(sr, 180, sip_180_Ringing); | |||||
2299 | } | |||||
2300 | } | |||||
2301 | ||||||
2302 | return 0; | |||||
2303 | } | |||||
2304 | ||||||
2305 | ||||||
2306 | /** @internal Respond to an INVITE request. | |||||
2307 | * | |||||
2308 | */ | |||||
2309 | static | |||||
2310 | int nua_invite_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||||
2311 | { | |||||
2312 | nua_handle_t *nh = sr->sr_owner; | |||||
2313 | nua_dialog_usage_t *du = sr->sr_usage; | |||||
2314 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
2315 | msg_t *msg = sr->sr_response.msg; | |||||
2316 | sip_t *sip = sr->sr_response.sip; | |||||
2317 | ||||||
2318 | int reliable = 0, maybe_answer = 0, offer = 0, answer = 0, extra = 0; | |||||
2319 | ||||||
2320 | enter(void)((((nua_log) != ((void*)0) && (nua_log)->log_init ) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 9 ? (_su_llog((nua_log), 9, "nua_session.c" , (const char *)__func__, 2320, "nua: %s: entering\n", __func__ )) : (void)0); | |||||
2321 | ||||||
2322 | if (du == NULL((void*)0)) { | |||||
2323 | if (sr->sr_status < 300) | |||||
2324 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||||
2325 | return nua_base_server_respond(sr, tags); | |||||
2326 | } | |||||
2327 | ||||||
2328 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||||
2329 | reliable = 1, maybe_answer = 1; | |||||
2330 | } | |||||
2331 | else if (nua_invite_server_is_100rel(sr, tags)) { | |||||
2332 | reliable = 1, maybe_answer = 1; | |||||
2333 | } | |||||
2334 | else if (!nh->nh_soa || sr->sr_status >= 300) { | |||||
2335 | if (sr->sr_neutral) | |||||
2336 | return nua_base_server_respond(sr, tags); | |||||
2337 | } | |||||
2338 | else if (tags && 100 < sr->sr_status && sr->sr_status < 200 && | |||||
2339 | !NHP_ISSET(nh->nh_prefs, early_answer)((nh->nh_prefs)->nhp_set_.set_bits.nhb_early_answer)) { | |||||
2340 | sdp_session_t const *user_sdp = NULL((void*)0); | |||||
2341 | char const *user_sdp_str = NULL((void*)0); | |||||
2342 | ||||||
2343 | tl_gets(tags, | |||||
2344 | SOATAG_USER_SDP_REF(user_sdp)soatag_user_sdp_ref, sdptag_session_vr(&(user_sdp)), | |||||
2345 | SOATAG_USER_SDP_STR_REF(user_sdp_str)soatag_user_sdp_str_ref, tag_str_vr(&(user_sdp_str)), | |||||
2346 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
2347 | ||||||
2348 | maybe_answer = user_sdp || user_sdp_str; | |||||
2349 | } | |||||
2350 | else { | |||||
2351 | maybe_answer = NH_PGET(nh, early_answer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_answer ? ((nh)->nh_prefs)->nhp_early_answer : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_answer); | |||||
2352 | } | |||||
2353 | ||||||
2354 | if (!nh->nh_soa) { | |||||
2355 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) { | |||||
2356 | if (sr->sr_offer_recv) | |||||
2357 | answer = 1; | |||||
2358 | else if (sr->sr_offer_sent < 2) | |||||
2359 | offer = 1; | |||||
2360 | } | |||||
2361 | } | |||||
2362 | else if (sr->sr_status >= 300) { | |||||
2363 | soa_clear_remote_sdp(nh->nh_soa); | |||||
2364 | } | |||||
2365 | else if (sr->sr_offer_sent && !sr->sr_answer_recv) | |||||
2366 | /* Wait for answer */; | |||||
2367 | else if (sr->sr_offer_recv && sr->sr_answer_sent > 1) { | |||||
2368 | /* We have sent answer */ | |||||
2369 | /* ... but we may want to send it again */ | |||||
2370 | tagi_t const *t = tl_find_last(tags, nutag_include_extra_sdp); | |||||
2371 | extra = t && t->t_value; | |||||
2372 | } | |||||
2373 | else if (sr->sr_offer_recv && !sr->sr_answer_sent && maybe_answer) { | |||||
2374 | /* Generate answer */ | |||||
2375 | if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) >= 0 && | |||||
2376 | soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||||
2377 | answer = 1; /* signal that O/A answer sent (answer to invite) */ | |||||
2378 | /* ss_sdp_version is updated only after answer is sent reliably */ | |||||
2379 | } | |||||
2380 | /* We have an error! */ | |||||
2381 | else if (sr->sr_status >= 200) { | |||||
2382 | sip_warning_t *warning = NULL((void*)0); | |||||
2383 | int wcode; | |||||
2384 | char const *text; | |||||
2385 | char const *host = "invalid."; | |||||
2386 | ||||||
2387 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
2388 | ||||||
2389 | wcode = soa_get_warning(nh->nh_soa, &text); | |||||
2390 | ||||||
2391 | if (wcode) { | |||||
2392 | if (sip->sip_contact) | |||||
2393 | host = sip->sip_contact->m_url->url_host; | |||||
2394 | warning = sip_warning_format(msg_home(msg)((su_home_t*)(msg)), "%u %s \"%s\"", | |||||
2395 | wcode, host, text); | |||||
2396 | sip_header_insert(msg, sip, (sip_header_t *)warning); | |||||
2397 | } | |||||
2398 | } | |||||
2399 | else { | |||||
2400 | /* 1xx - we don't have to send answer */ | |||||
2401 | } | |||||
2402 | } | |||||
2403 | else if (sr->sr_offer_recv && sr->sr_answer_sent == 1 && maybe_answer) { | |||||
2404 | /* The answer was sent unreliably, keep sending it */ | |||||
2405 | answer = 1; | |||||
2406 | } | |||||
2407 | else if (!sr->sr_offer_recv && !sr->sr_offer_sent && reliable) { | |||||
2408 | if (200 <= sr->sr_status && nua_callstate_ready <= ss->ss_state && | |||||
2409 | NH_PGET(nh, refresh_without_sdp)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresh_without_sdp ? ((nh)->nh_prefs)->nhp_refresh_without_sdp : ((nh)-> nh_nua->nua_handles->nh_prefs)->nhp_refresh_without_sdp )) | |||||
2410 | /* This is a re-INVITE without SDP - do not try to send offer in 200 */; | |||||
2411 | else | |||||
2412 | /* Generate offer */ | |||||
2413 | if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0) | |||||
2414 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
2415 | else | |||||
2416 | offer = 1; | |||||
2417 | } | |||||
2418 | ||||||
2419 | if (sr->sr_status < 300 && (offer || answer || extra)) { | |||||
2420 | if (nh->nh_soa && session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||||
2421 | SR_STATUS1(sr, SIP_500_INTERNAL_SERVER_ERROR)sr_status(sr, 500, sip_500_Internal_server_error); | |||||
2422 | else if (offer) | |||||
2423 | sr->sr_offer_sent = 1 + reliable, ss->ss_oa_sent = Offer; | |||||
2424 | else if (answer) | |||||
2425 | sr->sr_answer_sent = 1 + reliable, ss->ss_oa_sent = Answer; | |||||
2426 | ||||||
2427 | if (answer && reliable && nh->nh_soa) { | |||||
2428 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
2429 | } | |||||
2430 | } | |||||
2431 | ||||||
2432 | if (reliable && sr->sr_status < 200) { | |||||
2433 | sr->sr_response.msg = NULL((void*)0), sr->sr_response.sip = NULL((void*)0); | |||||
2434 | if (nta_reliable_mreply(sr->sr_irq, process_prack, sr, msg) == NULL((void*)0)) | |||||
2435 | return -1; | |||||
2436 | sr->sr_100rel = 1; | |||||
2437 | return 0; | |||||
2438 | } | |||||
2439 | ||||||
2440 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||||
2441 | session_timer_preferences(ss->ss_timer, | |||||
2442 | sip, | |||||
2443 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||||
2444 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||||
2445 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||||
2446 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||||
2447 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||||
2448 | ||||||
2449 | if (session_timer_is_supported(ss->ss_timer)) | |||||
2450 | session_timer_add_headers(ss->ss_timer, 0, msg, sip, nh); | |||||
2451 | } | |||||
2452 | ||||||
2453 | return nua_base_server_respond(sr, tags); | |||||
2454 | } | |||||
2455 | ||||||
2456 | /** Check if the response should be sent reliably. | |||||
2457 | * XXX - use tags to indicate when to use reliable responses ??? | |||||
2458 | */ | |||||
2459 | static | |||||
2460 | int nua_invite_server_is_100rel(nua_server_request_t *sr, tagi_t const *tags) | |||||
2461 | { | |||||
2462 | nua_handle_t *nh = sr->sr_owner; | |||||
2463 | sip_require_t *require = sr->sr_request.sip->sip_require; | |||||
2464 | sip_supported_t *supported = sr->sr_request.sip->sip_supported; | |||||
2465 | ||||||
2466 | if (sr->sr_status >= 200) | |||||
2467 | return 0; | |||||
2468 | else if (sr->sr_status == 100) | |||||
2469 | return 0; | |||||
2470 | ||||||
2471 | if (sip_has_feature(sr->sr_response.sip->sip_require, "100rel")) | |||||
2472 | return 1; | |||||
2473 | ||||||
2474 | if (require == NULL((void*)0) && supported == NULL((void*)0)) | |||||
2475 | return 0; | |||||
2476 | ||||||
2477 | if (!sip_has_feature(NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), "100rel")) | |||||
2478 | return 0; | |||||
2479 | if (sip_has_feature(require, "100rel")) | |||||
2480 | return 1; | |||||
2481 | if (!sip_has_feature(supported, "100rel")) | |||||
2482 | return 0; | |||||
2483 | if (sr->sr_status == 183) | |||||
2484 | return 1; | |||||
2485 | ||||||
2486 | if (NH_PGET(nh, early_media)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_early_media ? ( (nh)->nh_prefs)->nhp_early_media : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_early_media) && !NH_PGET(nh, only183_100rel)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_only183_100rel ? ((nh)->nh_prefs)->nhp_only183_100rel : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_only183_100rel)) | |||||
2487 | return 1; | |||||
2488 | ||||||
2489 | if (sip_has_feature(require, "precondition")) { | |||||
2490 | if (!NH_PGET(nh, only183_100rel)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_only183_100rel ? ((nh)->nh_prefs)->nhp_only183_100rel : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_only183_100rel)) | |||||
2491 | return 1; | |||||
2492 | if (sr->sr_offer_recv && !sr->sr_answer_sent) | |||||
2493 | return 1; | |||||
2494 | } | |||||
2495 | ||||||
2496 | return 0; | |||||
2497 | } | |||||
2498 | ||||||
2499 | ||||||
2500 | int nua_invite_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||||
2501 | { | |||||
2502 | nua_handle_t *nh = sr->sr_owner; | |||||
2503 | nua_dialog_usage_t *du = sr->sr_usage; | |||||
2504 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2505 | int initial = sr->sr_initial && !sr->sr_event; | |||||
2506 | int neutral = sr->sr_neutral; | |||||
2507 | int application = sr->sr_application; | |||||
2508 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||||
2509 | int retval; | |||||
2510 | ||||||
2511 | if (!sr->sr_event && status < 300) { /* Not reported yet */ | |||||
2512 | nta_incoming_bind(sr->sr_irq, process_ack_or_cancel, sr); | |||||
2513 | } | |||||
2514 | ||||||
2515 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||||
2516 | ||||||
2517 | if (retval >= 2 || ss == NULL((void*)0)) { | |||||
2518 | /* Session has been terminated. */ | |||||
2519 | if (!initial && !neutral) { | |||||
2520 | #if 0 | |||||
2521 | signal_call_state_change(nh, NULL((void*)0), status, phrase, | |||||
2522 | nua_callstate_terminated); | |||||
2523 | #endif | |||||
2524 | } | |||||
2525 | return retval; | |||||
2526 | } | |||||
2527 | ||||||
2528 | /* Update session state */ | |||||
2529 | if (status < 300 || application != 0) { | |||||
2530 | assert(ss->ss_state != nua_callstate_calling)((ss->ss_state != nua_callstate_calling) ? (void) (0) : __assert_fail ("ss->ss_state != nua_callstate_calling", "nua_session.c" , 2530, __PRETTY_FUNCTION__)); | |||||
2531 | assert(ss->ss_state != nua_callstate_proceeding)((ss->ss_state != nua_callstate_proceeding) ? (void) (0) : __assert_fail ("ss->ss_state != nua_callstate_proceeding" , "nua_session.c", 2531, __PRETTY_FUNCTION__)); | |||||
2532 | signal_call_state_change(nh, ss, status, phrase, | |||||
2533 | status >= 300 | |||||
2534 | ? nua_callstate_init | |||||
2535 | : status >= 200 | |||||
2536 | ? nua_callstate_completed | |||||
2537 | : status > 100 | |||||
2538 | ? nua_callstate_early | |||||
2539 | : nua_callstate_received); | |||||
2540 | } | |||||
2541 | ||||||
2542 | if (status == 180) | |||||
2543 | ss->ss_alerting = 1; | |||||
2544 | else if (status >= 200) | |||||
2545 | ss->ss_alerting = 0; | |||||
2546 | ||||||
2547 | if (200 <= status && status < 300) { | |||||
2548 | du->du_ready = 1; | |||||
2549 | } | |||||
2550 | else if (300 <= status && !neutral) { | |||||
2551 | if (nh->nh_soa) | |||||
2552 | soa_init_offer_answer(nh->nh_soa); | |||||
2553 | } | |||||
2554 | ||||||
2555 | if (ss->ss_state == nua_callstate_init) { | |||||
2556 | assert(status >= 300)((status >= 300) ? (void) (0) : __assert_fail ("status >= 300" , "nua_session.c", 2556, __PRETTY_FUNCTION__)); | |||||
2557 | nua_session_usage_destroy(nh, ss); | |||||
2558 | } | |||||
2559 | ||||||
2560 | return retval; | |||||
2561 | } | |||||
2562 | ||||||
2563 | /** @internal Process ACK or CANCEL or timeout (no ACK) for incoming INVITE */ | |||||
2564 | static | |||||
2565 | int process_ack_or_cancel(nua_server_request_t *sr, | |||||
2566 | nta_incoming_t *irq, | |||||
2567 | sip_t const *sip) | |||||
2568 | { | |||||
2569 | enter(void)((((nua_log) != ((void*)0) && (nua_log)->log_init ) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 9 ? (_su_llog((nua_log), 9, "nua_session.c" , (const char *)__func__, 2569, "nua: %s: entering\n", __func__ )) : (void)0); | |||||
2570 | ||||||
2571 | assert(sr->sr_usage)((sr->sr_usage) ? (void) (0) : __assert_fail ("sr->sr_usage" , "nua_session.c", 2571, __PRETTY_FUNCTION__)); | |||||
2572 | assert(sr->sr_usage->du_class == nua_session_usage)((sr->sr_usage->du_class == nua_session_usage) ? (void) (0) : __assert_fail ("sr->sr_usage->du_class == nua_session_usage" , "nua_session.c", 2572, __PRETTY_FUNCTION__)); | |||||
2573 | ||||||
2574 | if (sip && sip->sip_request->rq_method == sip_method_ack) | |||||
2575 | return process_ack(sr, irq, sip); | |||||
2576 | else if (sip && sip->sip_request->rq_method == sip_method_cancel) | |||||
2577 | return process_cancel(sr, irq, sip); | |||||
2578 | else | |||||
2579 | return process_timeout(sr, irq); | |||||
2580 | } | |||||
2581 | ||||||
2582 | /** @NUA_EVENT nua_i_ack | |||||
2583 | * | |||||
2584 | * Final response to INVITE has been acknowledged by UAC with ACK. | |||||
2585 | * | |||||
2586 | * @note This event is only sent after 2XX response. | |||||
2587 | * | |||||
2588 | * @param nh operation handle associated with the call | |||||
2589 | * @param hmagic application context associated with the call | |||||
2590 | * @param sip incoming ACK request | |||||
2591 | * @param tags empty | |||||
2592 | * | |||||
2593 | * @sa #nua_i_invite, #nua_i_state, @ref nua_uas_call_model, nua_ack() | |||||
2594 | * | |||||
2595 | * @END_NUA_EVENT | |||||
2596 | */ | |||||
2597 | static | |||||
2598 | int process_ack(nua_server_request_t *sr, | |||||
2599 | nta_incoming_t *irq, | |||||
2600 | sip_t const *sip) | |||||
2601 | { | |||||
2602 | nua_handle_t *nh = sr->sr_owner; | |||||
2603 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2604 | msg_t *msg = nta_incoming_getrequest_ackcancel(irq); | |||||
2605 | char const *recv = NULL((void*)0); | |||||
2606 | int uas; | |||||
2607 | ||||||
2608 | if (ss == NULL((void*)0)) | |||||
2609 | return 0; | |||||
2610 | ||||||
2611 | if (sr->sr_offer_sent && !sr->sr_answer_recv) { | |||||
2612 | char const *sdp; | |||||
2613 | size_t len; | |||||
2614 | ||||||
2615 | if (session_get_description(sip, &sdp, &len)) | |||||
2616 | recv = Answer; | |||||
2617 | ||||||
2618 | if (recv) { | |||||
2619 | assert(ss->ss_oa_recv == NULL)((ss->ss_oa_recv == ((void*)0)) ? (void) (0) : __assert_fail ("ss->ss_oa_recv == ((void*)0)", "nua_session.c", 2619, __PRETTY_FUNCTION__ )); | |||||
2620 | ss->ss_oa_recv = recv; | |||||
2621 | } | |||||
2622 | ||||||
2623 | if (nh->nh_soa == NULL((void*)0)) | |||||
2624 | ; | |||||
2625 | else if (recv == NULL((void*)0) ) { | |||||
2626 | if (ss->ss_state >= nua_callstate_ready && | |||||
2627 | soa_get_user_version(nh->nh_soa) == ss->ss_sdp_version && | |||||
2628 | soa_process_reject(nh->nh_soa, NULL((void*)0)) >= 0) { | |||||
2629 | url_t const *m; | |||||
2630 | ||||||
2631 | /* The re-INVITE was a refresh and re-INVITEr ignored our offer */ | |||||
2632 | ss->ss_oa_sent = NULL((void*)0); | |||||
2633 | ||||||
2634 | if (sr->sr_request.sip->sip_contact) | |||||
2635 | m = sr->sr_request.sip->sip_contact->m_url; | |||||
2636 | else | |||||
2637 | m = sr->sr_request.sip->sip_from->a_url; | |||||
2638 | ||||||
2639 | SU_DEBUG_3(("nua(%p): re-INVITEr ignored offer in our %u response "((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2641, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0) | |||||
2640 | "(Contact: <" URL_PRINT_FORMAT ">)\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2641, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0) | |||||
2641 | (void *)nh, sr->sr_status, URL_PRINT_ARGS(m)))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2641, "nua(%p): re-INVITEr ignored offer in our %u response " "(Contact: <" "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s" ">)\n" , (void *)nh, sr->sr_status, (m)->url_scheme ? (m)-> url_scheme : "", (m)->url_type != url_any && (m)-> url_scheme && (m)->url_scheme[0] ? ":" : "", (m)-> url_root && ((m)->url_host || (m)->url_user) ? "//" : "", (m)->url_user ? (m)->url_user : "", (m)->url_user && (m)->url_password ? ":" : "", (m)->url_user && (m)->url_password ? (m)->url_password : "", (m)->url_user && (m)->url_host ? "@" : "", (m) ->url_host ? (m)->url_host : "", (m)->url_host && (m)->url_port ? ":" : "", (m)->url_host && (m) ->url_port ? (m)->url_port : "", (m)->url_root && (m)->url_path ? "/" : "", (m)->url_path ? (m)->url_path : "", (m)->url_params ? ";" : "", (m)->url_params ? (m )->url_params : "", (m)->url_headers ? "?" : "", (m)-> url_headers ? (m)->url_headers : "", (m)->url_fragment ? "#" : "", (m)->url_fragment ? (m)->url_fragment : "")) : (void)0); | |||||
2642 | if (sr->sr_request.sip->sip_user_agent) | |||||
2643 | SU_DEBUG_3(("nua(%p): re-INVITE: \"User-Agent: %s\"\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2644, "nua(%p): re-INVITE: \"User-Agent: %s\"\n" , (void *)nh, sr->sr_request.sip->sip_user_agent->g_string )) : (void)0) | |||||
2644 | sr->sr_request.sip->sip_user_agent->g_string))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 2644, "nua(%p): re-INVITE: \"User-Agent: %s\"\n" , (void *)nh, sr->sr_request.sip->sip_user_agent->g_string )) : (void)0); | |||||
2645 | } | |||||
2646 | else | |||||
2647 | return process_ack_error(sr, msg, 488, "Offer-Answer error", | |||||
2648 | "SIP;cause=488;text=\"No answer to offer\""); | |||||
2649 | } | |||||
2650 | else if (soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sdp, len) >= 0 && | |||||
2651 | soa_process_answer(nh->nh_soa, NULL((void*)0)) >= 0 && | |||||
2652 | soa_activate(nh->nh_soa, NULL((void*)0)) >= 0) { | |||||
2653 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
2654 | } | |||||
2655 | else { | |||||
2656 | int status; char const *phrase, *reason; | |||||
2657 | ||||||
2658 | status = soa_error_as_sip_response(nh->nh_soa, &phrase); | |||||
2659 | reason = soa_error_as_sip_reason(nh->nh_soa); | |||||
2660 | ||||||
2661 | return process_ack_error(sr, msg, status, phrase, reason); | |||||
2662 | } | |||||
2663 | } | |||||
2664 | ||||||
2665 | if (nh->nh_soa) | |||||
2666 | soa_clear_remote_sdp(nh->nh_soa); | |||||
2667 | ||||||
2668 | nua_stack_event(nh->nh_nua, nh, msg, nua_i_ack, SIP_200_OK200, sip_200_OK, NULL((void*)0)); | |||||
2669 | signal_call_state_change(nh, ss, 200, "OK", nua_callstate_ready); | |||||
2670 | session_timer_set(ss, uas = 1); | |||||
2671 | ||||||
2672 | nua_server_request_destroy(sr); | |||||
2673 | ||||||
2674 | return 0; | |||||
2675 | } | |||||
2676 | ||||||
2677 | static int | |||||
2678 | process_ack_error(nua_server_request_t *sr, | |||||
2679 | msg_t *ackmsg, | |||||
2680 | int status, | |||||
2681 | char const *phrase, | |||||
2682 | char const *reason) | |||||
2683 | { | |||||
2684 | nua_handle_t *nh = sr->sr_owner; | |||||
2685 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2686 | int error; | |||||
2687 | ||||||
2688 | nua_stack_event(nh->nh_nua, nh, ackmsg, | |||||
2689 | nua_i_ack, status, phrase, NULL((void*)0)); | |||||
2690 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||||
2691 | nua_i_media_error, status, phrase, NULL((void*)0)); | |||||
2692 | ||||||
2693 | if (reason) ss->ss_reason = reason; | |||||
2694 | ss->ss_reporting = 1; | |||||
2695 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||||
2696 | ss->ss_reporting = 0; | |||||
2697 | ||||||
2698 | signal_call_state_change(nh, ss, | |||||
2699 | 488, "Offer-Answer Error", | |||||
2700 | /* We report terminated state if BYE failed */ | |||||
2701 | error | |||||
2702 | ? nua_callstate_terminated | |||||
2703 | : nua_callstate_terminating); | |||||
2704 | ||||||
2705 | return 0; | |||||
2706 | } | |||||
2707 | ||||||
2708 | ||||||
2709 | /** @NUA_EVENT nua_i_cancel | |||||
2710 | * | |||||
2711 | * Incoming INVITE has been cancelled by the client. | |||||
2712 | * | |||||
2713 | * @param status status code of response to CANCEL sent automatically by stack | |||||
2714 | * @param phrase a short textual description of @a status code | |||||
2715 | * @param nh operation handle associated with the call | |||||
2716 | * @param hmagic application context associated with the call | |||||
2717 | * @param sip incoming CANCEL request | |||||
2718 | * @param tags empty | |||||
2719 | * | |||||
2720 | * @sa @ref nua_uas_call_model, nua_cancel(), #nua_i_invite, #nua_i_state | |||||
2721 | * | |||||
2722 | * @END_NUA_EVENT | |||||
2723 | */ | |||||
2724 | ||||||
2725 | /* CANCEL */ | |||||
2726 | static | |||||
2727 | int process_cancel(nua_server_request_t *sr, | |||||
2728 | nta_incoming_t *irq, | |||||
2729 | sip_t const *sip) | |||||
2730 | { | |||||
2731 | nua_handle_t *nh = sr->sr_owner; | |||||
2732 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2733 | msg_t *cancel = nta_incoming_getrequest_ackcancel(irq); | |||||
2734 | ||||||
2735 | assert(ss)((ss) ? (void) (0) : __assert_fail ("ss", "nua_session.c", 2735 , __PRETTY_FUNCTION__)); assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((ss == nua_session_usage_for_dialog(nh->nh_ds)) ? (void) ( 0) : __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 2735, __PRETTY_FUNCTION__)); (void)ss; | |||||
2736 | ||||||
2737 | assert(nta_incoming_status(irq) < 200)((nta_incoming_status(irq) < 200) ? (void) (0) : __assert_fail ("nta_incoming_status(irq) < 200", "nua_session.c", 2737, __PRETTY_FUNCTION__)); | |||||
2738 | ||||||
2739 | nua_stack_event(nh->nh_nua, nh, cancel, nua_i_cancel, SIP_200_OK200, sip_200_OK, NULL((void*)0)); | |||||
2740 | sr->sr_application = SR_STATUS1(sr, SIP_487_REQUEST_TERMINATED)sr_status(sr, 487, sip_487_Request_terminated); | |||||
2741 | nua_server_respond(sr, NULL((void*)0)); | |||||
2742 | nua_server_report(sr); | |||||
2743 | ||||||
2744 | return 0; | |||||
2745 | } | |||||
2746 | ||||||
2747 | /* Timeout (no ACK or PRACK received) */ | |||||
2748 | static | |||||
2749 | int process_timeout(nua_server_request_t *sr, | |||||
2750 | nta_incoming_t *irq) | |||||
2751 | { | |||||
2752 | nua_handle_t *nh = sr->sr_owner; | |||||
2753 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2754 | char const *phrase = "ACK Timeout"; | |||||
2755 | char const *reason = "SIP;cause=408;text=\"ACK Timeout\""; | |||||
2756 | int error; | |||||
2757 | ||||||
2758 | assert(ss)((ss) ? (void) (0) : __assert_fail ("ss", "nua_session.c", 2758 , __PRETTY_FUNCTION__)); assert(ss == nua_session_usage_for_dialog(nh->nh_ds))((ss == nua_session_usage_for_dialog(nh->nh_ds)) ? (void) ( 0) : __assert_fail ("ss == nua_session_usage_for_dialog(nh->nh_ds)" , "nua_session.c", 2758, __PRETTY_FUNCTION__)); | |||||
2759 | ||||||
2760 | if (nua_server_request_is_pending(sr)) { | |||||
2761 | phrase = "PRACK Timeout"; | |||||
2762 | reason = "SIP;cause=504;text=\"PRACK Timeout\""; | |||||
2763 | } | |||||
2764 | ||||||
2765 | nua_stack_event(nh->nh_nua, nh, 0, nua_i_error, 408, phrase, NULL((void*)0)); | |||||
2766 | ||||||
2767 | if (nua_server_request_is_pending(sr)) { | |||||
2768 | /* PRACK timeout */ | |||||
2769 | SR_STATUS1(sr, SIP_504_GATEWAY_TIME_OUT)sr_status(sr, 504, sip_504_Gateway_time_out); | |||||
2770 | nua_server_trespond(sr, | |||||
2771 | SIPTAG_REASON_STR(reason)siptag_reason_str, tag_str_v(reason), | |||||
2772 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
2773 | if (nua_server_report(sr) >= 2) | |||||
2774 | return 0; /* Done */ | |||||
2775 | sr = NULL((void*)0); | |||||
2776 | } | |||||
2777 | ||||||
2778 | /* send BYE, too, if 200 OK (or 183 to re-INVITE) timeouts */ | |||||
2779 | ss->ss_reason = reason; | |||||
2780 | ||||||
2781 | ss->ss_reporting = 1; /* We report terminated state here if BYE fails */ | |||||
2782 | error = nua_client_create(nh, nua_r_bye, &nua_bye_client_methods, NULL((void*)0)); | |||||
2783 | ss->ss_reporting = 0; | |||||
2784 | ||||||
2785 | signal_call_state_change(nh, ss, 0, phrase, | |||||
2786 | error | |||||
2787 | ? nua_callstate_terminated | |||||
2788 | : nua_callstate_terminating); | |||||
2789 | ||||||
2790 | if (sr) | |||||
2791 | nua_server_request_destroy(sr); | |||||
2792 | ||||||
2793 | return 0; | |||||
2794 | } | |||||
2795 | ||||||
2796 | ||||||
2797 | /** @NUA_EVENT nua_i_prack | |||||
2798 | * | |||||
2799 | * Incoming PRACK request. PRACK request is used to acknowledge reliable | |||||
2800 | * preliminary responses and it is usually sent automatically by the nua | |||||
2801 | * stack. | |||||
2802 | * | |||||
2803 | * @param status status code of response sent automatically by stack | |||||
2804 | * @param phrase a short textual description of @a status code | |||||
2805 | * @param nh operation handle associated with the call | |||||
2806 | * @param hmagic application context associated with the call | |||||
2807 | * @param sip incoming PRACK request | |||||
2808 | * @param tags empty | |||||
2809 | * | |||||
2810 | * @sa nua_prack(), #nua_r_prack, @RFC3262, NUTAG_EARLY_MEDIA() | |||||
2811 | * | |||||
2812 | * @END_NUA_EVENT | |||||
2813 | */ | |||||
2814 | ||||||
2815 | int nua_prack_server_init(nua_server_request_t *sr); | |||||
2816 | int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||||
2817 | int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags); | |||||
2818 | ||||||
2819 | nua_server_methods_t const nua_prack_server_methods = | |||||
2820 | { | |||||
2821 | SIP_METHOD_PRACKsip_method_prack, "PRACK", | |||||
2822 | nua_i_prack, /* Event */ | |||||
2823 | { | |||||
2824 | 0, /* Do not create dialog */ | |||||
2825 | 1, /* In-dialog request */ | |||||
2826 | 1, /* Target refresh request */ | |||||
2827 | 1, /* Add Contact */ | |||||
2828 | }, | |||||
2829 | nua_prack_server_init, | |||||
2830 | nua_base_server_preprocess((void*)0), | |||||
2831 | nua_base_server_params((void*)0), | |||||
2832 | nua_prack_server_respond, | |||||
2833 | nua_prack_server_report, | |||||
2834 | }; | |||||
2835 | ||||||
2836 | /** @internal Process reliable response PRACK or (timeout from 100rel) */ | |||||
2837 | static int process_prack(nua_server_request_t *sr, | |||||
2838 | nta_reliable_t *rel, | |||||
2839 | nta_incoming_t *irq, | |||||
2840 | sip_t const *sip) | |||||
2841 | { | |||||
2842 | nua_handle_t *nh; | |||||
2843 | ||||||
2844 | nta_reliable_destroy(rel); | |||||
2845 | ||||||
2846 | if (irq == NULL((void*)0)) | |||||
2847 | /* Final response interrupted 100rel, we did not actually receive PRACK */ | |||||
2848 | return 200; | |||||
2849 | ||||||
2850 | sr->sr_pracked = 1; | |||||
2851 | ||||||
2852 | if (!nua_server_request_is_pending(sr)) /* There is no INVITE anymore */ | |||||
2853 | return 481; | |||||
2854 | ||||||
2855 | nh = sr->sr_owner; | |||||
2856 | ||||||
2857 | if (nh->nh_ds->ds_leg == NULL((void*)0)) | |||||
2858 | return 500; | |||||
2859 | ||||||
2860 | if (sip == NULL((void*)0)) { | |||||
2861 | /* 100rel timeout */ | |||||
2862 | SR_STATUS(sr, 504, "Reliable Response Timeout")((sr)->sr_phrase = ("Reliable Response Timeout"), (sr)-> sr_status = (504)); | |||||
2863 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), nua_i_error, | |||||
2864 | sr->sr_status, sr->sr_phrase, | |||||
2865 | NULL((void*)0)); | |||||
2866 | nua_server_trespond(sr, | |||||
2867 | SIPTAG_REASON_STR("SIP;cause=504;"siptag_reason_str, tag_str_v("SIP;cause=504;" "text=\"PRACK Timeout\"" ) | |||||
2868 | "text=\"PRACK Timeout\"")siptag_reason_str, tag_str_v("SIP;cause=504;" "text=\"PRACK Timeout\"" ), | |||||
2869 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
2870 | nua_server_report(sr); | |||||
2871 | return 504; | |||||
2872 | } | |||||
2873 | ||||||
2874 | nta_incoming_bind(irq, NULL((void*)0), (void *)sr); | |||||
2875 | ||||||
2876 | return nua_stack_process_request(nh, nh->nh_ds->ds_leg, irq, sip); | |||||
2877 | } | |||||
2878 | ||||||
2879 | ||||||
2880 | int nua_prack_server_init(nua_server_request_t *sr) | |||||
2881 | { | |||||
2882 | nua_handle_t *nh = sr->sr_owner; | |||||
2883 | nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL((void*)0)); | |||||
2884 | ||||||
2885 | if (sri == NULL((void*)0)) | |||||
2886 | return SR_STATUS(sr, 481, "No Such Preliminary Response")((sr)->sr_phrase = ("No Such Preliminary Response"), (sr)-> sr_status = (481)); | |||||
2887 | ||||||
2888 | if (nua_session_server_init(sr)) | |||||
2889 | return sr->sr_status; | |||||
2890 | ||||||
2891 | if (sr->sr_sdp) { | |||||
2892 | nua_session_usage_t *ss = NUA_DIALOG_USAGE_PRIVATE(sr->sr_usage)((void *)((sr->sr_usage) + 1)); | |||||
2893 | char const *offeranswer; | |||||
2894 | ||||||
2895 | /* XXX - check for overlap? */ | |||||
2896 | ||||||
2897 | if (sri->sr_offer_sent && !sri->sr_answer_recv) | |||||
2898 | sr->sr_answer_recv = 1, sri->sr_answer_recv = 1, offeranswer = Answer; | |||||
2899 | else | |||||
2900 | sr->sr_offer_recv = 1, offeranswer = Offer; | |||||
2901 | ||||||
2902 | ss->ss_oa_recv = offeranswer; | |||||
2903 | ||||||
2904 | if (nh->nh_soa && | |||||
2905 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||||
2906 | SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2907, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "PRACK", offeranswer)) : (void)0) | |||||
2907 | "PRACK", offeranswer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2907, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "PRACK", offeranswer)) : (void)0); | |||||
2908 | return | |||||
2909 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
2910 | } | |||||
2911 | } | |||||
2912 | ||||||
2913 | return 0; | |||||
2914 | } | |||||
2915 | ||||||
2916 | int nua_prack_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||||
2917 | { | |||||
2918 | nua_handle_t *nh = sr->sr_owner; | |||||
2919 | ||||||
2920 | if (sr->sr_status < 200 || 300 <= sr->sr_status) | |||||
2921 | return nua_base_server_respond(sr, tags); | |||||
2922 | ||||||
2923 | if (sr->sr_sdp) { | |||||
2924 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2925 | msg_t *msg = sr->sr_response.msg; | |||||
2926 | sip_t *sip = sr->sr_response.sip; | |||||
2927 | ||||||
2928 | if (nh->nh_soa == NULL((void*)0)) { | |||||
2929 | if (sr->sr_offer_recv && session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||||
2930 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||||
2931 | } | |||||
2932 | else if ((sr->sr_offer_recv && soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0) || | |||||
2933 | (sr->sr_answer_recv && soa_process_answer(nh->nh_soa, NULL((void*)0)) < 0)) { | |||||
2934 | SU_DEBUG_5(("nua(%p): %s server: %s %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2937, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||||
2935 | (void *)nh, "PRACK",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2937, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||||
2936 | "error processing",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2937, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0) | |||||
2937 | sr->sr_offer_recv ? Offer : Answer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 2937, "nua(%p): %s server: %s %s\n" , (void *)nh, "PRACK", "error processing", sr->sr_offer_recv ? Offer : Answer)) : (void)0); | |||||
2938 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
2939 | } | |||||
2940 | else if (sr->sr_offer_recv) { | |||||
2941 | if (session_include_description(nh->nh_soa, 1, msg, sip) < 0) | |||||
2942 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||||
2943 | else | |||||
2944 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||||
2945 | } | |||||
2946 | } | |||||
2947 | ||||||
2948 | return nua_base_server_respond(sr, tags); | |||||
2949 | } | |||||
2950 | ||||||
2951 | int nua_prack_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||||
2952 | { | |||||
2953 | nua_handle_t *nh = sr->sr_owner; | |||||
2954 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
2955 | nua_server_request_t *sri = nta_incoming_magic(sr->sr_irq, NULL((void*)0)); | |||||
2956 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||||
2957 | int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent || sr->sr_offer_sent || sr->sr_answer_recv; | |||||
2958 | int retval; | |||||
2959 | ||||||
2960 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||||
2961 | ||||||
2962 | if (retval >= 2 || ss == NULL((void*)0)) { | |||||
2963 | #if 0 | |||||
2964 | signal_call_state_change(nh, NULL((void*)0), | |||||
2965 | status, phrase, | |||||
2966 | nua_callstate_terminated); | |||||
2967 | #endif | |||||
2968 | return retval; | |||||
2969 | } | |||||
2970 | ||||||
2971 | if (offer_recv_or_answer_sent) { | |||||
2972 | /* signal offer received, answer sent */ | |||||
2973 | signal_call_state_change(nh, ss, | |||||
2974 | status, phrase, | |||||
2975 | ss->ss_state); | |||||
2976 | if (nh->nh_soa) { | |||||
2977 | soa_activate(nh->nh_soa, NULL((void*)0)); | |||||
2978 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
2979 | } | |||||
2980 | } | |||||
2981 | ||||||
2982 | if (status < 200 || 300 <= status) | |||||
2983 | return retval; | |||||
2984 | ||||||
2985 | assert(sri)((sri) ? (void) (0) : __assert_fail ("sri", "nua_session.c", 2985 , __PRETTY_FUNCTION__)); | |||||
2986 | ||||||
2987 | if (sri == NULL((void*)0)) { | |||||
2988 | ||||||
2989 | } | |||||
2990 | else if (SR_HAS_SAVED_SIGNAL(sri)((sri)->sr_signal[0] != ((void*)0))) { | |||||
2991 | nua_signal_data_t const *e; | |||||
2992 | ||||||
2993 | e = nua_signal_data(sri->sr_signal); | |||||
2994 | ||||||
2995 | sri->sr_application = SR_STATUS(sri, e->e_status, e->e_phrase)((sri)->sr_phrase = (e->e_phrase), (sri)->sr_status = (e->e_status)); | |||||
2996 | ||||||
2997 | nua_server_params(sri, e->e_tags); | |||||
2998 | nua_server_respond(sri, e->e_tags); | |||||
2999 | nua_server_report(sri); | |||||
3000 | } | |||||
3001 | else if (ss->ss_state < nua_callstate_ready | |||||
3002 | && !ss->ss_alerting | |||||
3003 | && !ss->ss_precondition | |||||
3004 | && NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||||
3005 | SR_STATUS1(sri, SIP_180_RINGING)sr_status(sri, 180, sip_180_Ringing); | |||||
3006 | nua_server_respond(sri, NULL((void*)0)); | |||||
3007 | nua_server_report(sri); | |||||
3008 | } | |||||
3009 | ||||||
3010 | return retval; | |||||
3011 | } | |||||
3012 | ||||||
3013 | /* ---------------------------------------------------------------------- */ | |||||
3014 | /* Automatic notifications from a referral */ | |||||
3015 | ||||||
3016 | static int | |||||
3017 | nh_referral_check(nua_handle_t *nh, tagi_t const *tags) | |||||
3018 | { | |||||
3019 | sip_event_t const *event = NULL((void*)0); | |||||
3020 | int pause = 1; | |||||
3021 | struct nua_referral *ref = nh->nh_referral; | |||||
3022 | nua_handle_t *ref_handle = ref->ref_handle; | |||||
3023 | ||||||
3024 | if (!ref_handle | |||||
3025 | && | |||||
3026 | tl_gets(tags, | |||||
3027 | NUTAG_NOTIFY_REFER_REF(ref_handle)nutag_notify_refer_ref, nutag_handle_vr(&(ref_handle)), | |||||
3028 | NUTAG_REFER_EVENT_REF(event)nutag_refer_event_ref, siptag_event_vr(&(event)), | |||||
3029 | NUTAG_REFER_PAUSE_REF(pause)nutag_refer_pause_ref, tag_bool_vr(&(pause)), | |||||
3030 | TAG_END()(tag_type_t)0, (tag_value_t)0) == 0 | |||||
3031 | && | |||||
3032 | tl_gets(nh->nh_tags, | |||||
3033 | NUTAG_NOTIFY_REFER_REF(ref_handle)nutag_notify_refer_ref, nutag_handle_vr(&(ref_handle)), | |||||
3034 | NUTAG_REFER_EVENT_REF(event)nutag_refer_event_ref, siptag_event_vr(&(event)), | |||||
3035 | NUTAG_REFER_PAUSE_REF(pause)nutag_refer_pause_ref, tag_bool_vr(&(pause)), | |||||
3036 | TAG_END()(tag_type_t)0, (tag_value_t)0) == 0) | |||||
3037 | return 0; | |||||
3038 | ||||||
3039 | if (!ref_handle) | |||||
3040 | return 0; | |||||
3041 | ||||||
3042 | /* Remove nh_referral and nh_notevent */ | |||||
3043 | tl_tremove(nh->nh_tags, | |||||
3044 | NUTAG_NOTIFY_REFER(ref_handle)nutag_notify_refer, nutag_handle_v(ref_handle), | |||||
3045 | TAG_IF(event, NUTAG_REFER_EVENT(event))!(event) ? tag_skip : nutag_refer_event, siptag_event_v(event ), | |||||
3046 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
3047 | ||||||
3048 | if (event) | |||||
3049 | ref->ref_event = sip_event_dup(nh->nh_home, event); | |||||
3050 | ||||||
3051 | if (!nh_validate(nh->nh_nua, ref_handle)) { | |||||
3052 | SU_DEBUG_3(("nua: invalid NOTIFY_REFER handle\n" VA_NONE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 3052, "nua: invalid NOTIFY_REFER handle\n" "%s", "")) : (void)0); | |||||
3053 | return -1; | |||||
3054 | } | |||||
3055 | else if (!ref->ref_event) { | |||||
3056 | SU_DEBUG_3(("nua: NOTIFY event missing\n" VA_NONE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 3056, "nua: NOTIFY event missing\n" "%s", "")) : (void)0); | |||||
3057 | return -1; | |||||
3058 | } | |||||
3059 | ||||||
3060 | if (ref_handle != ref->ref_handle) { | |||||
3061 | if (ref->ref_handle) | |||||
3062 | nua_handle_unref(ref->ref_handle); | |||||
3063 | ref->ref_handle = nua_handle_ref(ref_handle); | |||||
3064 | } | |||||
3065 | ||||||
3066 | #if 0 | |||||
3067 | if (pause) { | |||||
3068 | /* Pause media on REFER handle */ | |||||
3069 | nmedia_pause(nua, ref_handle->nh_nm, NULL((void*)0)); | |||||
3070 | } | |||||
3071 | #endif | |||||
3072 | ||||||
3073 | return 0; | |||||
3074 | } | |||||
3075 | ||||||
3076 | static void | |||||
3077 | nh_referral_respond(nua_handle_t *nh, int status, char const *phrase) | |||||
3078 | { | |||||
3079 | char payload[128]; | |||||
3080 | char const *substate; | |||||
3081 | struct nua_referral *ref = nh->nh_referral; | |||||
3082 | ||||||
3083 | if (!nh_validate(nh->nh_nua, ref->ref_handle)) { | |||||
3084 | if (ref) { | |||||
3085 | if (ref->ref_handle) | |||||
3086 | SU_DEBUG_1(("nh_handle_referral: stale referral handle %p\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 3087, "nh_handle_referral: stale referral handle %p\n" , (void *)ref->ref_handle)) : (void)0) | |||||
3087 | (void *)ref->ref_handle))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 1 ? (_su_llog((nua_log), 1, "nua_session.c" , (const char *)__func__, 3087, "nh_handle_referral: stale referral handle %p\n" , (void *)ref->ref_handle)) : (void)0); | |||||
3088 | ref->ref_handle = NULL((void*)0); | |||||
3089 | } | |||||
3090 | return; | |||||
3091 | } | |||||
3092 | ||||||
3093 | /* XXX - we should have a policy here whether to send 101..199 */ | |||||
3094 | ||||||
3095 | assert(ref->ref_event)((ref->ref_event) ? (void) (0) : __assert_fail ("ref->ref_event" , "nua_session.c", 3095, __PRETTY_FUNCTION__)); | |||||
3096 | ||||||
3097 | if (status >= 300) | |||||
3098 | status = 503, phrase = sip_503_Service_unavailable; | |||||
3099 | ||||||
3100 | snprintf(payload, sizeof(payload), "SIP/2.0 %03u %s\r\n", status, phrase); | |||||
3101 | ||||||
3102 | if (status < 200) | |||||
3103 | substate = "active"; | |||||
3104 | else | |||||
3105 | substate = "terminated ;reason=noresource"; | |||||
3106 | ||||||
3107 | nua_stack_post_signal(ref->ref_handle, | |||||
3108 | nua_r_notify, | |||||
3109 | SIPTAG_EVENT(ref->ref_event)siptag_event, siptag_event_v(ref->ref_event), | |||||
3110 | SIPTAG_SUBSCRIPTION_STATE_STR(substate)siptag_subscription_state_str, tag_str_v(substate), | |||||
3111 | SIPTAG_CONTENT_TYPE_STR("message/sipfrag")siptag_content_type_str, tag_str_v("message/sipfrag"), | |||||
3112 | SIPTAG_PAYLOAD_STR(payload)siptag_payload_str, tag_str_v(payload), | |||||
3113 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
3114 | ||||||
3115 | if (status < 200) | |||||
3116 | return; | |||||
3117 | ||||||
3118 | su_free(nh->nh_home, ref->ref_event), ref->ref_event = NULL((void*)0); | |||||
3119 | ||||||
3120 | nua_handle_unref(ref->ref_handle), ref->ref_handle = NULL((void*)0); | |||||
3121 | } | |||||
3122 | ||||||
3123 | /* ======================================================================== */ | |||||
3124 | /* INFO */ | |||||
3125 | ||||||
3126 | /**@fn void nua_info(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
3127 | * | |||||
3128 | * Send an INFO request. | |||||
3129 | * | |||||
3130 | * INFO is used to send call related information like DTMF | |||||
3131 | * digit input events. See @RFC2976. | |||||
3132 | * | |||||
3133 | * @param nh Pointer to operation handle | |||||
3134 | * @param tag, value, ... List of tagged parameters | |||||
3135 | * | |||||
3136 | * @return | |||||
3137 | * nothing | |||||
3138 | * | |||||
3139 | * @par Related Tags: | |||||
3140 | * Header tags defined in <sofia-sip/sip_tag.h>. | |||||
3141 | * | |||||
3142 | * @par Events: | |||||
3143 | * #nua_r_info | |||||
3144 | * | |||||
3145 | * @sa #nua_i_info | |||||
3146 | */ | |||||
3147 | ||||||
3148 | nua_client_methods_t const nua_info_client_methods = { | |||||
3149 | SIP_METHOD_INFOsip_method_info, "INFO", /* crm_method, crm_method_name */ | |||||
3150 | 0, /* crm_extra */ | |||||
3151 | { /* crm_flags */ | |||||
3152 | /* create_dialog */ 0, | |||||
3153 | /* in_dialog */ 1, | |||||
3154 | /* target refresh */ 0 | |||||
3155 | }, | |||||
3156 | NULL((void*)0), /* crm_template */ | |||||
3157 | NULL((void*)0), /* crm_init */ | |||||
3158 | NULL((void*)0), /* crm_send */ | |||||
3159 | NULL((void*)0), /* crm_check_restart */ | |||||
3160 | NULL((void*)0), /* crm_recv */ | |||||
3161 | NULL((void*)0), /* crm_preliminary */ | |||||
3162 | NULL((void*)0), /* crm_report */ | |||||
3163 | NULL((void*)0), /* crm_complete */ | |||||
3164 | }; | |||||
3165 | ||||||
3166 | int | |||||
3167 | nua_stack_info(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) | |||||
3168 | { | |||||
3169 | return nua_client_create(nh, e, &nua_info_client_methods, tags); | |||||
3170 | } | |||||
3171 | ||||||
3172 | /** @NUA_EVENT nua_r_info | |||||
3173 | * | |||||
3174 | * Response to an outgoing @b INFO request. | |||||
3175 | * | |||||
3176 | * @param status response status code | |||||
3177 | * (if the request is retried, @a status is 100, the @a | |||||
3178 | * sip->sip_status->st_status contain the real status code | |||||
3179 | * from the response message, e.g., 302, 401, or 407) | |||||
3180 | * @param phrase a short textual description of @a status code | |||||
3181 | * @param nh operation handle associated with the call | |||||
3182 | * @param hmagic application context associated with the call | |||||
3183 | * @param sip response to @b INFO or NULL upon an error | |||||
3184 | * (status code is in @a status and | |||||
3185 | * descriptive message in @a phrase parameters) | |||||
3186 | * @param tags empty | |||||
3187 | * | |||||
3188 | * @sa nua_info(), #nua_i_info, @RFC2976 | |||||
3189 | * | |||||
3190 | * @END_NUA_EVENT | |||||
3191 | */ | |||||
3192 | ||||||
3193 | /** @NUA_EVENT nua_i_info | |||||
3194 | * | |||||
3195 | * Incoming session INFO request. | |||||
3196 | * | |||||
3197 | * @param status statuscode of response sent automatically by stack | |||||
3198 | * @param phrase a short textual description of @a status code | |||||
3199 | * @param nh operation handle associated with the call | |||||
3200 | * @param hmagic application context associated with the call | |||||
3201 | * @param sip incoming INFO request | |||||
3202 | * @param tags empty | |||||
3203 | * | |||||
3204 | * @sa nua_info(), #nua_r_info, @RFC2976 | |||||
3205 | * | |||||
3206 | * @END_NUA_EVENT | |||||
3207 | */ | |||||
3208 | ||||||
3209 | nua_server_methods_t const nua_info_server_methods = | |||||
3210 | { | |||||
3211 | SIP_METHOD_INFOsip_method_info, "INFO", | |||||
3212 | nua_i_info, /* Event */ | |||||
3213 | { | |||||
3214 | 0, /* Do not create dialog */ | |||||
3215 | 0, /* Allow outside dialog, too */ | |||||
3216 | 0, /* Not a target refresh request */ | |||||
3217 | 0, /* Do not add Contact */ | |||||
3218 | }, | |||||
3219 | nua_base_server_init((void*)0), | |||||
3220 | nua_base_server_preprocess((void*)0), | |||||
3221 | nua_base_server_params((void*)0), | |||||
3222 | nua_base_server_respond, | |||||
3223 | nua_base_server_report, | |||||
3224 | }; | |||||
3225 | ||||||
3226 | /* ======================================================================== */ | |||||
3227 | /* UPDATE */ | |||||
3228 | ||||||
3229 | /**@fn void nua_update(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
3230 | * | |||||
3231 | * Update a session. | |||||
3232 | * | |||||
3233 | * Update a session using SIP UPDATE method. See @RFC3311. | |||||
3234 | * | |||||
3235 | * Update method can be used when the session has been established with | |||||
3236 | * INVITE. It's mainly used during the session establishment when | |||||
3237 | * preconditions are used (@RFC3312). It can be also used during the call if | |||||
3238 | * no user input is needed for offer/answer negotiation. | |||||
3239 | * | |||||
3240 | * @param nh Pointer to operation handle | |||||
3241 | * @param tag, value, ... List of tagged parameters | |||||
3242 | * | |||||
3243 | * @return | |||||
3244 | * nothing | |||||
3245 | * | |||||
3246 | * @par Related Tags: | |||||
3247 | * same as nua_invite() | |||||
3248 | * | |||||
3249 | * @par Events: | |||||
3250 | * #nua_r_update \n | |||||
3251 | * #nua_i_state (#nua_i_active, #nua_i_terminated)\n | |||||
3252 | * #nua_i_media_error \n | |||||
3253 | * | |||||
3254 | * @sa @ref nua_call_model, @RFC3311, nua_update(), #nua_i_update | |||||
3255 | */ | |||||
3256 | ||||||
3257 | static int nua_update_client_init(nua_client_request_t *cr, | |||||
3258 | msg_t *msg, sip_t *sip, | |||||
3259 | tagi_t const *tags); | |||||
3260 | static int nua_update_client_request(nua_client_request_t *cr, | |||||
3261 | msg_t *msg, sip_t *sip, | |||||
3262 | tagi_t const *tags); | |||||
3263 | static int nua_update_client_response(nua_client_request_t *cr, | |||||
3264 | int status, char const *phrase, | |||||
3265 | sip_t const *sip); | |||||
3266 | static int nua_update_client_report(nua_client_request_t *cr, | |||||
3267 | int status, char const *phrase, | |||||
3268 | sip_t const *sip, | |||||
3269 | nta_outgoing_t *orq, | |||||
3270 | tagi_t const *tags); | |||||
3271 | ||||||
3272 | nua_client_methods_t const nua_update_client_methods = { | |||||
3273 | SIP_METHOD_UPDATEsip_method_update, "UPDATE", /* crm_method, crm_method_name */ | |||||
3274 | 0, /* crm_extrasize of private data */ | |||||
3275 | { /* crm_flags */ | |||||
3276 | /* create_dialog */ 0, | |||||
3277 | /* in_dialog */ 1, | |||||
3278 | /* target refresh */ 1 | |||||
3279 | }, | |||||
3280 | NULL((void*)0), /* crm_template */ | |||||
3281 | nua_update_client_init, /* crm_init */ | |||||
3282 | nua_update_client_request, /* crm_send */ | |||||
3283 | session_timer_check_restart, /* crm_check_restart */ | |||||
3284 | nua_update_client_response, /* crm_recv */ | |||||
3285 | NULL((void*)0), /* crm_preliminary */ | |||||
3286 | nua_update_client_report, /* crm_report */ | |||||
3287 | NULL((void*)0), /* crm_complete */ | |||||
3288 | }; | |||||
3289 | ||||||
3290 | int nua_stack_update(nua_t *nua, nua_handle_t *nh, nua_event_t e, | |||||
3291 | tagi_t const *tags) | |||||
3292 | { | |||||
3293 | return nua_client_create(nh, e, &nua_update_client_methods, tags); | |||||
3294 | } | |||||
3295 | ||||||
3296 | static int nua_update_client_init(nua_client_request_t *cr, | |||||
3297 | msg_t *msg, sip_t *sip, | |||||
3298 | tagi_t const *tags) | |||||
3299 | { | |||||
3300 | nua_handle_t *nh = cr->cr_owner; | |||||
3301 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
3302 | ||||||
3303 | cr->cr_usage = du; | |||||
3304 | ||||||
3305 | return 0; | |||||
3306 | } | |||||
3307 | ||||||
3308 | static int nua_update_client_request(nua_client_request_t *cr, | |||||
3309 | msg_t *msg, sip_t *sip, | |||||
3310 | tagi_t const *tags) | |||||
3311 | { | |||||
3312 | nua_handle_t *nh = cr->cr_owner; | |||||
3313 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3314 | nua_session_usage_t *ss; | |||||
3315 | nua_server_request_t *sr; | |||||
3316 | nua_client_request_t *cri; | |||||
3317 | int offer_sent = 0, retval; | |||||
3318 | ||||||
3319 | if (du == NULL((void*)0)) /* Call terminated */ | |||||
3320 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||||
3321 | ||||||
3322 | ss = NUA_DIALOG_USAGE_PRIVATE(du)((void *)((du) + 1)); | |||||
3323 | if (ss->ss_state >= nua_callstate_terminating) | |||||
3324 | return nua_client_return(cr, 900, "Session is terminating", msg); | |||||
3325 | ||||||
3326 | cri = du->du_cr; | |||||
3327 | ||||||
3328 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||||
3329 | if ((sr->sr_offer_sent && !sr->sr_answer_recv) || | |||||
3330 | (sr->sr_offer_recv && !sr->sr_answer_sent)) | |||||
3331 | break; | |||||
3332 | ||||||
3333 | if (nh->nh_soa == NULL((void*)0)) { | |||||
3334 | offer_sent = session_get_description(sip, NULL((void*)0), NULL((void*)0)); | |||||
3335 | } | |||||
3336 | else if (sr || | |||||
3337 | (cri && cri->cr_offer_sent && !cri->cr_answer_recv) || | |||||
3338 | (cri && cri->cr_offer_recv && !cri->cr_answer_sent)) { | |||||
3339 | if (session_get_description(sip, NULL((void*)0), NULL((void*)0))) | |||||
3340 | return nua_client_return(cr, 500, "Overlapping Offer/Answer", msg); | |||||
3341 | } | |||||
3342 | else if (!sip->sip_payload) { | |||||
3343 | soa_init_offer_answer(nh->nh_soa); | |||||
3344 | ||||||
3345 | if (soa_generate_offer(nh->nh_soa, 0, NULL((void*)0)) < 0 || | |||||
3346 | session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||||
3347 | if (ss->ss_state < nua_callstate_ready) { | |||||
3348 | /* XXX - use soa_error_as_sip_reason(nh->nh_soa) */ | |||||
3349 | cr->cr_graceful = 1; | |||||
3350 | ss->ss_reason = "SIP;cause=400;text=\"Local media failure\""; | |||||
3351 | } | |||||
3352 | return nua_client_return(cr, 900, "Local media failed", msg); | |||||
3353 | } | |||||
3354 | offer_sent = 1; | |||||
3355 | } | |||||
3356 | ||||||
3357 | /* Add session timer headers */ | |||||
3358 | session_timer_preferences(ss->ss_timer, | |||||
3359 | sip, | |||||
3360 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||||
3361 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||||
3362 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||||
3363 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||||
3364 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||||
3365 | ||||||
3366 | if (session_timer_is_supported(ss->ss_timer)) | |||||
3367 | session_timer_add_headers(ss->ss_timer, ss->ss_state < nua_callstate_ready, | |||||
3368 | msg, sip, nh); | |||||
3369 | ||||||
3370 | retval = nua_base_client_request(cr, msg, sip, NULL((void*)0)); | |||||
3371 | ||||||
3372 | if (retval == 0) { | |||||
3373 | enum nua_callstate state = ss->ss_state; | |||||
3374 | cr->cr_offer_sent = offer_sent; | |||||
3375 | ss->ss_update_needed = 0; | |||||
3376 | ||||||
3377 | if (state == nua_callstate_ready) | |||||
3378 | state = nua_callstate_calling; /* XXX */ | |||||
3379 | ||||||
3380 | if (offer_sent) | |||||
3381 | ss->ss_oa_sent = Offer; | |||||
3382 | ||||||
3383 | if (!cr->cr_restarting) /* Restart logic calls nua_update_client_report */ | |||||
3384 | signal_call_state_change(nh, ss, 0, "UPDATE sent", state); | |||||
3385 | } | |||||
3386 | ||||||
3387 | return retval; | |||||
3388 | } | |||||
3389 | ||||||
3390 | static int nua_update_client_response(nua_client_request_t *cr, | |||||
3391 | int status, char const *phrase, | |||||
3392 | sip_t const *sip) | |||||
3393 | { | |||||
3394 | nua_handle_t *nh = cr->cr_owner; | |||||
3395 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3396 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3397 | int uas; | |||||
3398 | ||||||
3399 | assert(200 <= status)((200 <= status) ? (void) (0) : __assert_fail ("200 <= status" , "nua_session.c", 3399, __PRETTY_FUNCTION__)); | |||||
3400 | ||||||
3401 | if (ss && sip && status < 300) { | |||||
3402 | if (session_timer_is_supported(ss->ss_timer)) { | |||||
3403 | nua_server_request_t *sr; | |||||
3404 | ||||||
3405 | for (sr = nh->nh_ds->ds_sr; sr; sr = sr->sr_next) | |||||
3406 | if (sr->sr_method == sip_method_invite || | |||||
3407 | sr->sr_method == sip_method_update) | |||||
3408 | break; | |||||
3409 | ||||||
3410 | if (!sr && (!du->du_cr || !du->du_cr->cr_orq)) { | |||||
3411 | session_timer_store(ss->ss_timer, sip); | |||||
3412 | session_timer_set(ss, uas = 0); | |||||
3413 | } | |||||
3414 | } | |||||
3415 | } | |||||
3416 | ||||||
3417 | return nua_session_client_response(cr, status, phrase, sip); | |||||
3418 | } | |||||
3419 | ||||||
3420 | /** @NUA_EVENT nua_r_update | |||||
3421 | * | |||||
3422 | * Answer to outgoing UPDATE. | |||||
3423 | * | |||||
3424 | * The UPDATE may be sent explicitly by nua_update() or | |||||
3425 | * implicitly by NUA state machine. | |||||
3426 | * | |||||
3427 | * @param status response status code | |||||
3428 | * (if the request is retried, @a status is 100, the @a | |||||
3429 | * sip->sip_status->st_status contain the real status code | |||||
3430 | * from the response message, e.g., 302, 401, or 407) | |||||
3431 | * @param phrase a short textual description of @a status code | |||||
3432 | * @param nh operation handle associated with the call | |||||
3433 | * @param hmagic application context associated with the call | |||||
3434 | * @param sip response to UPDATE request or NULL upon an error | |||||
3435 | * (status code is in @a status and | |||||
3436 | * descriptive message in @a phrase parameters) | |||||
3437 | * @param tags empty | |||||
3438 | * | |||||
3439 | * @sa @ref nua_call_model, @RFC3311, nua_update(), #nua_i_update | |||||
3440 | * | |||||
3441 | * @END_NUA_EVENT | |||||
3442 | */ | |||||
3443 | ||||||
3444 | static int nua_update_client_report(nua_client_request_t *cr, | |||||
3445 | int status, char const *phrase, | |||||
3446 | sip_t const *sip, | |||||
3447 | nta_outgoing_t *orq, | |||||
3448 | tagi_t const *tags) | |||||
3449 | { | |||||
3450 | nua_handle_t *nh = cr->cr_owner; | |||||
3451 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3452 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3453 | ||||||
3454 | nua_stack_event(nh->nh_nua, nh, | |||||
3455 | nta_outgoing_getresponse(orq), | |||||
3456 | (enum nua_event_e)cr->cr_event, | |||||
3457 | status, phrase, | |||||
3458 | tags); | |||||
3459 | ||||||
3460 | if (!ss || cr->cr_terminated || cr->cr_graceful || cr->cr_waiting) | |||||
3461 | return 1; | |||||
3462 | ||||||
3463 | if (cr->cr_offer_sent) { | |||||
3464 | unsigned next_state = ss->ss_state; | |||||
3465 | ||||||
3466 | if (status < 200) | |||||
3467 | ; | |||||
3468 | else if (nua_invite_client_should_ack(du->du_cr)) { | |||||
3469 | /* There is an un-ACK-ed INVITE there */ | |||||
3470 | assert(du->du_cr->cr_method == sip_method_invite)((du->du_cr->cr_method == sip_method_invite) ? (void) ( 0) : __assert_fail ("du->du_cr->cr_method == sip_method_invite" , "nua_session.c", 3470, __PRETTY_FUNCTION__)); | |||||
3471 | ||||||
3472 | if (NH_PGET(nh, auto_ack)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack ? ((nh )->nh_prefs)->nhp_auto_ack : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_auto_ack) || | |||||
3473 | /* Auto-ACK response to re-INVITE when media is enabled | |||||
3474 | and auto_ack is not set to 0 on handle */ | |||||
3475 | (ss->ss_state == nua_callstate_ready && nh->nh_soa && | |||||
3476 | !NH_PISSET(nh, auto_ack)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_ack) && (nh)->nh_nua->nua_handles->nh_prefs != (nh)->nh_prefs ))) { | |||||
3477 | if (nua_invite_client_ack(du->du_cr, NULL((void*)0)) > 0) | |||||
3478 | next_state = nua_callstate_ready; | |||||
3479 | else | |||||
3480 | next_state = nua_callstate_terminating; | |||||
3481 | } | |||||
3482 | } | |||||
3483 | ||||||
3484 | signal_call_state_change(nh, ss, status, phrase, (enum nua_callstate)next_state); | |||||
3485 | } | |||||
3486 | ||||||
3487 | return 1; | |||||
3488 | } | |||||
3489 | ||||||
3490 | /* ---------------------------------------------------------------------- */ | |||||
3491 | /* UPDATE server */ | |||||
3492 | ||||||
3493 | int nua_update_server_init(nua_server_request_t *sr); | |||||
3494 | int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||||
3495 | int nua_update_server_report(nua_server_request_t *, tagi_t const *); | |||||
3496 | ||||||
3497 | nua_server_methods_t const nua_update_server_methods = | |||||
3498 | { | |||||
3499 | SIP_METHOD_UPDATEsip_method_update, "UPDATE", | |||||
3500 | nua_i_update, /* Event */ | |||||
3501 | { | |||||
3502 | 0, /* Do not create dialog */ | |||||
3503 | 1, /* In-dialog request */ | |||||
3504 | 1, /* Target refresh request */ | |||||
3505 | 1, /* Add Contact */ | |||||
3506 | }, | |||||
3507 | nua_update_server_init, | |||||
3508 | nua_base_server_preprocess((void*)0), | |||||
3509 | nua_base_server_params((void*)0), | |||||
3510 | nua_update_server_respond, | |||||
3511 | nua_update_server_report, | |||||
3512 | }; | |||||
3513 | ||||||
3514 | int nua_update_server_init(nua_server_request_t *sr) | |||||
3515 | { | |||||
3516 | nua_handle_t *nh = sr->sr_owner; | |||||
3517 | nua_session_usage_t *ss; | |||||
3518 | ||||||
3519 | sip_t const *request = sr->sr_request.sip; | |||||
3520 | ||||||
3521 | if (nua_session_server_init(sr)) | |||||
3522 | return sr->sr_status; | |||||
3523 | ||||||
3524 | ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
3525 | ||||||
3526 | /* Do session timer negotiation */ | |||||
3527 | if (request->sip_session_expires) | |||||
3528 | session_timer_store(ss->ss_timer, request); | |||||
3529 | ||||||
3530 | if (sr->sr_sdp) { /* Check for overlap */ | |||||
3531 | nua_client_request_t *cr; | |||||
3532 | nua_server_request_t *sr0; | |||||
3533 | int overlap = 0; | |||||
3534 | ||||||
3535 | /* | |||||
3536 | A UAS that receives an UPDATE before it has generated a final | |||||
3537 | response to a previous UPDATE on the same dialog MUST return a 500 | |||||
3538 | response to the new UPDATE, and MUST include a Retry-After header | |||||
3539 | field with a randomly chosen value between 0 and 10 seconds. | |||||
3540 | ||||||
3541 | If an UPDATE is received that contains an offer, and the UAS has | |||||
3542 | generated an offer (in an UPDATE, PRACK or INVITE) to which it has | |||||
3543 | not yet received an answer, the UAS MUST reject the UPDATE with a 491 | |||||
3544 | response. Similarly, if an UPDATE is received that contains an | |||||
3545 | offer, and the UAS has received an offer (in an UPDATE, PRACK, or | |||||
3546 | INVITE) to which it has not yet generated an answer, the UAS MUST | |||||
3547 | reject the UPDATE with a 500 response, and MUST include a Retry-After | |||||
3548 | header field with a randomly chosen value between 0 and 10 seconds. | |||||
3549 | */ | |||||
3550 | for (cr = nh->nh_ds->ds_cr; cr; cr = cr->cr_next) | |||||
3551 | if ((overlap = cr->cr_offer_sent && !cr->cr_answer_recv)) | |||||
3552 | break; | |||||
3553 | ||||||
3554 | if (!overlap) | |||||
3555 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) | |||||
3556 | if ((overlap = sr0->sr_offer_recv && !sr0->sr_answer_sent)) | |||||
3557 | break; | |||||
3558 | ||||||
3559 | if (nh->nh_soa && overlap) { | |||||
3560 | return nua_server_retry_after(sr, 500, "Overlapping Offer/Answer", 1, 9); | |||||
3561 | } | |||||
3562 | ||||||
3563 | if (nh->nh_soa && | |||||
3564 | soa_set_remote_sdp(nh->nh_soa, NULL((void*)0), sr->sr_sdp, sr->sr_sdp_len) < 0) { | |||||
3565 | SU_DEBUG_5(("nua(%p): %s server: error parsing %s\n", (void *)nh,((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3566, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "UPDATE", Offer)) : (void)0) | |||||
3566 | "UPDATE", Offer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3566, "nua(%p): %s server: error parsing %s\n" , (void *)nh, "UPDATE", Offer)) : (void)0); | |||||
3567 | return | |||||
3568 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
3569 | } | |||||
3570 | ||||||
3571 | sr->sr_offer_recv = 1; | |||||
3572 | ss ? ss->ss_oa_recv = Offer : Offer; | |||||
3573 | } | |||||
3574 | ||||||
3575 | return 0; | |||||
3576 | } | |||||
3577 | ||||||
3578 | /** @internal Respond to an UPDATE request. | |||||
3579 | * | |||||
3580 | */ | |||||
3581 | int nua_update_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||||
3582 | { | |||||
3583 | nua_handle_t *nh = sr->sr_owner; | |||||
3584 | nua_session_usage_t *ss = nua_dialog_usage_private(sr->sr_usage)((sr->sr_usage) ? (void*)((sr->sr_usage) + 1) : ((void* )0)); | |||||
3585 | msg_t *msg = sr->sr_response.msg; | |||||
3586 | sip_t *sip = sr->sr_response.sip; | |||||
3587 | ||||||
3588 | if (200 <= sr->sr_status && sr->sr_status < 300 && sr->sr_sdp) { | |||||
3589 | if (nh->nh_soa == NULL((void*)0)) { | |||||
3590 | sr->sr_answer_sent = 1, ss ? ss->ss_oa_sent = Answer : Answer; | |||||
3591 | } | |||||
3592 | else if (soa_generate_answer(nh->nh_soa, NULL((void*)0)) < 0) { | |||||
3593 | SU_DEBUG_5(("nua(%p): %s server: %s %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3594, "nua(%p): %s server: %s %s\n" , (void *)nh, "UPDATE", "error processing", Offer)) : (void)0 ) | |||||
3594 | (void *)nh, "UPDATE", "error processing", Offer))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3594, "nua(%p): %s server: %s %s\n" , (void *)nh, "UPDATE", "error processing", Offer)) : (void)0 ); | |||||
3595 | sr->sr_status = soa_error_as_sip_response(nh->nh_soa, &sr->sr_phrase); | |||||
3596 | } | |||||
3597 | else if (soa_activate(nh->nh_soa, NULL((void*)0)) < 0) { | |||||
3598 | SU_DEBUG_5(("nua(%p): %s server: error activating media\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3599, "nua(%p): %s server: error activating media\n" , (void *)nh, "UPDATE")) : (void)0) | |||||
3599 | (void *)nh, "UPDATE"))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 3599, "nua(%p): %s server: error activating media\n" , (void *)nh, "UPDATE")) : (void)0); | |||||
3600 | /* XXX */ | |||||
3601 | } | |||||
3602 | else if (session_include_description(nh->nh_soa, 1, msg, sip) < 0) { | |||||
3603 | sr_status(sr, SIP_500_INTERNAL_SERVER_ERROR500, sip_500_Internal_server_error); | |||||
3604 | } | |||||
3605 | else { | |||||
3606 | sr->sr_answer_sent = 1, ss->ss_oa_sent = Answer; | |||||
3607 | ss->ss_sdp_version = soa_get_user_version(nh->nh_soa); | |||||
3608 | } | |||||
3609 | } | |||||
3610 | ||||||
3611 | if (ss && 200 <= sr->sr_status && sr->sr_status < 300) { | |||||
3612 | session_timer_preferences(ss->ss_timer, | |||||
3613 | sip, | |||||
3614 | NH_PGET(nh, supported)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_supported ? (( nh)->nh_prefs)->nhp_supported : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_supported), | |||||
3615 | NH_PGET(nh, session_timer)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_session_timer ? ((nh)->nh_prefs)->nhp_session_timer : ((nh)->nh_nua ->nua_handles->nh_prefs)->nhp_session_timer), | |||||
3616 | NUA_PISSET(nh->nh_nua, nh, session_timer)((((nh->nh_nua)->nua_handles->nh_prefs)->nhp_set_ .set_bits.nhb_session_timer) || ((nh) && (((nh)->nh_prefs )->nhp_set_.set_bits.nhb_session_timer))), | |||||
3617 | NH_PGET(nh, refresher)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_refresher ? (( nh)->nh_prefs)->nhp_refresher : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_refresher), | |||||
3618 | NH_PGET(nh, min_se)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_min_se ? ((nh) ->nh_prefs)->nhp_min_se : ((nh)->nh_nua->nua_handles ->nh_prefs)->nhp_min_se)); | |||||
3619 | ||||||
3620 | if (session_timer_is_supported(ss->ss_timer)) { | |||||
3621 | nua_server_request_t *sr0; | |||||
3622 | int uas; | |||||
3623 | ||||||
3624 | session_timer_add_headers(ss->ss_timer, 0, msg, sip, nh); | |||||
3625 | ||||||
3626 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr0->sr_next) | |||||
3627 | if (sr0->sr_method == sip_method_invite) | |||||
3628 | break; | |||||
3629 | ||||||
3630 | if (!sr0 && (!sr->sr_usage->du_cr || !sr->sr_usage->du_cr->cr_orq)) | |||||
3631 | session_timer_set(ss, uas = 1); | |||||
3632 | } | |||||
3633 | } | |||||
3634 | ||||||
3635 | return nua_base_server_respond(sr, tags); | |||||
3636 | } | |||||
3637 | ||||||
3638 | /** @NUA_EVENT nua_i_update | |||||
3639 | * | |||||
3640 | * @brief Incoming session UPDATE request. | |||||
3641 | * | |||||
3642 | * @param status statuscode of response sent automatically by stack | |||||
3643 | * @param phrase a short textual description of @a status code | |||||
3644 | * @param nh operation handle associated with the call | |||||
3645 | * @param hmagic application context associated with the call | |||||
3646 | * @param sip incoming UPDATE request | |||||
3647 | * @param tags empty | |||||
3648 | * | |||||
3649 | * @sa nua_update(), #nua_r_update, #nua_i_state | |||||
3650 | * | |||||
3651 | * @END_NUA_EVENT | |||||
3652 | */ | |||||
3653 | ||||||
3654 | int nua_update_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||||
3655 | { | |||||
3656 | nua_handle_t *nh = sr->sr_owner; | |||||
3657 | nua_dialog_usage_t *du = sr->sr_usage; | |||||
3658 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3659 | int status = sr->sr_status; char const *phrase = sr->sr_phrase; | |||||
3660 | int offer_recv_or_answer_sent = sr->sr_offer_recv || sr->sr_answer_sent; | |||||
3661 | int retval; | |||||
3662 | ||||||
3663 | retval = nua_base_server_report(sr, tags), sr = NULL((void*)0); /* destroys sr */ | |||||
3664 | ||||||
3665 | if (retval >= 2 || ss == NULL((void*)0)) { | |||||
3666 | #if 0 | |||||
3667 | signal_call_state_change(nh, NULL((void*)0), status, phrase, | |||||
3668 | nua_callstate_terminated); | |||||
3669 | #endif | |||||
3670 | return retval; | |||||
3671 | } | |||||
3672 | ||||||
3673 | if (offer_recv_or_answer_sent) { | |||||
3674 | /* signal offer received, answer sent */ | |||||
3675 | enum nua_callstate state = ss->ss_state; | |||||
3676 | ||||||
3677 | if (state == nua_callstate_ready && status < 200) | |||||
3678 | state = nua_callstate_received; | |||||
3679 | ||||||
3680 | signal_call_state_change(nh, ss, status, phrase, state); | |||||
3681 | } | |||||
3682 | ||||||
3683 | if (200 <= status && status < 300 | |||||
3684 | && ss->ss_state < nua_callstate_ready | |||||
3685 | && ss->ss_precondition | |||||
3686 | && !ss->ss_alerting | |||||
3687 | && NH_PGET(nh, auto_alert)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_auto_alert ? ( (nh)->nh_prefs)->nhp_auto_alert : ((nh)->nh_nua-> nua_handles->nh_prefs)->nhp_auto_alert)) { | |||||
3688 | nua_server_request_t *sri; | |||||
3689 | ||||||
3690 | for (sri = nh->nh_ds->ds_sr; sri; sri = sri->sr_next) | |||||
3691 | if (sri->sr_method == sip_method_invite && | |||||
3692 | nua_server_request_is_pending(sri)) | |||||
3693 | break; | |||||
3694 | ||||||
3695 | if (sri) { | |||||
3696 | SR_STATUS1(sri, SIP_180_RINGING)sr_status(sri, 180, sip_180_Ringing); | |||||
3697 | nua_server_respond(sri, NULL((void*)0)); | |||||
3698 | nua_server_report(sri); | |||||
3699 | } | |||||
3700 | } | |||||
3701 | ||||||
3702 | return retval; | |||||
3703 | } | |||||
3704 | ||||||
3705 | /* ======================================================================== */ | |||||
3706 | /* BYE */ | |||||
3707 | ||||||
3708 | /**@fn void nua_bye(nua_handle_t *nh, tag_type_t tag, tag_value_t value, ...); | |||||
3709 | * | |||||
3710 | * Hangdown a call. | |||||
3711 | * | |||||
3712 | * Hangdown a call using SIP BYE method. Also the media session | |||||
3713 | * associated with the call is terminated. | |||||
3714 | * | |||||
3715 | * @param nh Pointer to operation handle | |||||
3716 | * @param tag, value, ... List of tagged parameters | |||||
3717 | * | |||||
3718 | * @return | |||||
3719 | * nothing | |||||
3720 | * | |||||
3721 | * @par Related Tags: | |||||
3722 | * none | |||||
3723 | * | |||||
3724 | * @par Events: | |||||
3725 | * #nua_r_bye \n | |||||
3726 | * #nua_i_media_error | |||||
3727 | */ | |||||
3728 | ||||||
3729 | static int nua_bye_client_init(nua_client_request_t *cr, | |||||
3730 | msg_t *msg, sip_t *sip, | |||||
3731 | tagi_t const *tags); | |||||
3732 | static int nua_bye_client_request(nua_client_request_t *cr, | |||||
3733 | msg_t *msg, sip_t *sip, | |||||
3734 | tagi_t const *tags); | |||||
3735 | static int nua_bye_client_response(nua_client_request_t *cr, | |||||
3736 | int status, char const *phrase, | |||||
3737 | sip_t const *sip); | |||||
3738 | static int nua_bye_client_report(nua_client_request_t *cr, | |||||
3739 | int status, char const *phrase, | |||||
3740 | sip_t const *sip, | |||||
3741 | nta_outgoing_t *orq, | |||||
3742 | tagi_t const *tags); | |||||
3743 | ||||||
3744 | nua_client_methods_t const nua_bye_client_methods = { | |||||
3745 | SIP_METHOD_BYEsip_method_bye, "BYE", /* crm_method, crm_method_name */ | |||||
3746 | 0, /* crm_extrasize */ | |||||
3747 | { /* crm_flags */ | |||||
3748 | /* create_dialog */ 0, | |||||
3749 | /* in_dialog */ 1, | |||||
3750 | /* target refresh */ 0 | |||||
3751 | }, | |||||
3752 | NULL((void*)0), /* crm_template */ | |||||
3753 | nua_bye_client_init, /* crm_init */ | |||||
3754 | nua_bye_client_request, /* crm_send */ | |||||
3755 | NULL((void*)0), /* crm_check_restart */ | |||||
3756 | nua_bye_client_response, /* crm_recv */ | |||||
3757 | NULL((void*)0), /* crm_preliminary */ | |||||
3758 | nua_bye_client_report, /* crm_report */ | |||||
3759 | NULL((void*)0), /* crm_complete */ | |||||
3760 | }; | |||||
3761 | ||||||
3762 | int | |||||
3763 | nua_stack_bye(nua_t *nua, nua_handle_t *nh, nua_event_t e, tagi_t const *tags) | |||||
3764 | { | |||||
3765 | nua_session_usage_t *ss = nua_session_usage_for_dialog(nh->nh_ds); | |||||
3766 | ||||||
3767 | if (ss && | |||||
3768 | nua_callstate_calling <= ss->ss_state && | |||||
3769 | ss->ss_state <= nua_callstate_proceeding) | |||||
3770 | return nua_client_create(nh, e, &nua_cancel_client_methods, tags); | |||||
3771 | else | |||||
3772 | return nua_client_create(nh, e, &nua_bye_client_methods, tags); | |||||
3773 | } | |||||
3774 | ||||||
3775 | static int nua_bye_client_init(nua_client_request_t *cr, | |||||
3776 | msg_t *msg, sip_t *sip, | |||||
3777 | tagi_t const *tags) | |||||
3778 | { | |||||
3779 | nua_handle_t *nh = cr->cr_owner; | |||||
3780 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
3781 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3782 | ||||||
3783 | if (!ss || (ss->ss_state >= nua_callstate_terminating && !cr->cr_auto)) | |||||
3784 | return nua_client_return(cr, 900, "Invalid handle for BYE", msg); | |||||
3785 | ||||||
3786 | if (!cr->cr_auto) | |||||
3787 | /* Implicit state transition by nua_bye() */ | |||||
3788 | ss->ss_state = nua_callstate_terminating; | |||||
3789 | ||||||
3790 | if (nh->nh_soa) | |||||
3791 | soa_terminate(nh->nh_soa, 0); | |||||
3792 | ||||||
3793 | nua_client_bind(cr, du); | |||||
3794 | ||||||
3795 | return 0; | |||||
3796 | } | |||||
3797 | ||||||
3798 | static int nua_bye_client_request(nua_client_request_t *cr, | |||||
3799 | msg_t *msg, sip_t *sip, | |||||
3800 | tagi_t const *tags) | |||||
3801 | { | |||||
3802 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3803 | nua_session_usage_t *ss; | |||||
3804 | char const *reason = NULL((void*)0); | |||||
3805 | ||||||
3806 | int error; | |||||
3807 | nua_server_request_t *sr; | |||||
3808 | ||||||
3809 | if (du == NULL((void*)0)) | |||||
3810 | return nua_client_return(cr, SIP_481_NO_TRANSACTION481, sip_481_No_transaction, msg); | |||||
3811 | ||||||
3812 | ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3813 | reason = ss->ss_reason; | |||||
3814 | ||||||
3815 | error = nua_base_client_trequest(cr, msg, sip, | |||||
3816 | SIPTAG_REASON_STR(reason)siptag_reason_str, tag_str_v(reason), | |||||
3817 | TAG_NEXT(tags)tag_next, (tag_value_t)(tags)); | |||||
3818 | ||||||
3819 | if (error == 0) { | |||||
3820 | nua_dialog_usage_reset_refresh(du); | |||||
3821 | ss->ss_timer->timer_set = 0; | |||||
3822 | ||||||
3823 | /* Terminate server transactions associated with session, too. */ | |||||
3824 | for (sr = du->du_dialog->ds_sr; sr; sr = sr->sr_next) { | |||||
3825 | if (sr->sr_usage == du && nua_server_request_is_pending(sr) && | |||||
3826 | sr->sr_method != sip_method_bye) { | |||||
3827 | sr_status(sr, SIP_486_BUSY_HERE486, sip_486_Busy_here); | |||||
3828 | nua_server_respond(sr, 0); | |||||
3829 | } | |||||
3830 | } | |||||
3831 | } | |||||
3832 | ||||||
3833 | return error; | |||||
3834 | } | |||||
3835 | static int nua_bye_client_response(nua_client_request_t *cr, | |||||
3836 | int status, char const *phrase, | |||||
3837 | sip_t const *sip) { | |||||
3838 | ||||||
3839 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3840 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3841 | ||||||
3842 | if (ss && ss->ss_reporting && status >= 900) | |||||
3843 | return 1; | |||||
3844 | ||||||
3845 | return nua_base_client_response(cr, status, phrase, sip, NULL((void*)0)); | |||||
3846 | } | |||||
3847 | ||||||
3848 | /** @NUA_EVENT nua_r_bye | |||||
3849 | * | |||||
3850 | * Answer to outgoing BYE. | |||||
3851 | * | |||||
3852 | * The BYE may be sent explicitly by nua_bye() or implicitly by NUA state | |||||
3853 | * machine. | |||||
3854 | * | |||||
3855 | * @param status response status code | |||||
3856 | * (if the request is retried, @a status is 100, the @a | |||||
3857 | * sip->sip_status->st_status contain the real status code | |||||
3858 | * from the response message, e.g., 302, 401, or 407) | |||||
3859 | * @param phrase a short textual description of @a status code | |||||
3860 | * @param nh operation handle associated with the call | |||||
3861 | * @param hmagic application context associated with the call | |||||
3862 | * @param sip response to BYE request or NULL upon an error | |||||
3863 | * (status code is in @a status and | |||||
3864 | * descriptive message in @a phrase parameters) | |||||
3865 | * @param tags empty | |||||
3866 | * | |||||
3867 | * @sa nua_bye(), @ref nua_call_model, #nua_i_state, #nua_r_invite() | |||||
3868 | * | |||||
3869 | * @END_NUA_EVENT | |||||
3870 | */ | |||||
3871 | ||||||
3872 | static int nua_bye_client_report(nua_client_request_t *cr, | |||||
3873 | int status, char const *phrase, | |||||
3874 | sip_t const *sip, | |||||
3875 | nta_outgoing_t *orq, | |||||
3876 | tagi_t const *tags) | |||||
3877 | { | |||||
3878 | nua_handle_t *nh = cr->cr_owner; | |||||
3879 | nua_dialog_usage_t *du = cr->cr_usage; | |||||
3880 | ||||||
3881 | nua_stack_event(nh->nh_nua, nh, | |||||
3882 | nta_outgoing_getresponse(orq), | |||||
3883 | (enum nua_event_e)cr->cr_event, | |||||
3884 | status, phrase, | |||||
3885 | tags); | |||||
3886 | ||||||
3887 | if (du == NULL((void*)0)) { | |||||
3888 | /* No more session */ | |||||
3889 | } | |||||
3890 | else if (status < 200) { | |||||
3891 | /* Preliminary */ | |||||
3892 | } | |||||
3893 | else { | |||||
3894 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3895 | nua_client_request_t *cri; | |||||
3896 | ||||||
3897 | if (ss->ss_reporting) { | |||||
3898 | return 1; /* Somebody else's problem */ | |||||
3899 | } | |||||
3900 | else if (cr->cr_waiting) { | |||||
3901 | return 1; /* Application problem */ | |||||
3902 | } | |||||
3903 | ||||||
3904 | nua_client_bind(cr, NULL((void*)0)); | |||||
3905 | ||||||
3906 | signal_call_state_change(nh, ss, status, "to BYE", | |||||
3907 | nua_callstate_terminated); | |||||
3908 | ||||||
3909 | for (cri = du->du_dialog->ds_cr; cri; cri = cri->cr_next) { | |||||
3910 | if (cri->cr_method == sip_method_invite) | |||||
3911 | break; | |||||
3912 | } | |||||
3913 | ||||||
3914 | if (!cri || cri->cr_status >= 200) { | |||||
3915 | /* INVITE is completed, we can zap the session... */; | |||||
3916 | nua_session_usage_destroy(nh, ss); | |||||
3917 | } | |||||
3918 | } | |||||
3919 | ||||||
3920 | return 1; | |||||
3921 | } | |||||
3922 | ||||||
3923 | /** @NUA_EVENT nua_i_bye | |||||
3924 | * | |||||
3925 | * Incoming BYE request, call hangup. | |||||
3926 | * | |||||
3927 | * @param status statuscode of response sent automatically by stack | |||||
3928 | * @param phrase a short textual description of @a status code | |||||
3929 | * @param nh operation handle associated with the call | |||||
3930 | * @param hmagic application context associated with the call | |||||
3931 | * @param sip pointer to BYE request | |||||
3932 | * @param tags empty | |||||
3933 | * | |||||
3934 | * @sa @ref nua_call_model, #nua_i_state, nua_bye(), nua_bye(), #nua_r_cancel | |||||
3935 | * | |||||
3936 | * @END_NUA_EVENT | |||||
3937 | */ | |||||
3938 | ||||||
3939 | int nua_bye_server_init(nua_server_request_t *sr); | |||||
3940 | int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags); | |||||
3941 | ||||||
3942 | nua_server_methods_t const nua_bye_server_methods = | |||||
3943 | { | |||||
3944 | SIP_METHOD_BYEsip_method_bye, "BYE", | |||||
3945 | nua_i_bye, /* Event */ | |||||
3946 | { | |||||
3947 | 0, /* Do not create dialog */ | |||||
3948 | 1, /* In-dialog request */ | |||||
3949 | 0, /* Not a target refresh request */ | |||||
3950 | 0, /* Do not add Contact */ | |||||
3951 | }, | |||||
3952 | nua_bye_server_init, | |||||
3953 | nua_base_server_preprocess((void*)0), | |||||
3954 | nua_base_server_params((void*)0), | |||||
3955 | nua_base_server_respond, | |||||
3956 | nua_bye_server_report, | |||||
3957 | }; | |||||
3958 | ||||||
3959 | ||||||
3960 | int nua_bye_server_init(nua_server_request_t *sr) | |||||
3961 | { | |||||
3962 | nua_handle_t *nh = sr->sr_owner; | |||||
3963 | nua_dialog_usage_t *du = nua_dialog_usage_for_session(nh->nh_ds); | |||||
3964 | ||||||
3965 | sr->sr_terminating = 1; | |||||
3966 | ||||||
3967 | if (du) | |||||
3968 | sr->sr_usage = du; | |||||
3969 | else | |||||
3970 | return SR_STATUS(sr, 481, "No Such Call")((sr)->sr_phrase = ("No Such Call"), (sr)->sr_status = ( 481)); | |||||
3971 | ||||||
3972 | return 0; | |||||
3973 | } | |||||
3974 | ||||||
3975 | int nua_bye_server_report(nua_server_request_t *sr, tagi_t const *tags) | |||||
3976 | { | |||||
3977 | nua_handle_t *nh = sr->sr_owner; | |||||
3978 | nua_dialog_usage_t *du = sr->sr_usage; | |||||
3979 | nua_session_usage_t *ss = nua_dialog_usage_private(du)((du) ? (void*)((du) + 1) : ((void*)0)); | |||||
3980 | int early = 0, retval; | |||||
3981 | ||||||
3982 | if (sr->sr_status < 200) | |||||
3983 | return nua_base_server_report(sr, tags); | |||||
3984 | ||||||
3985 | if (ss) { | |||||
3986 | nua_server_request_t *sr0 = NULL((void*)0), *sr_next; | |||||
3987 | char const *phrase; | |||||
3988 | ||||||
3989 | early = ss->ss_state < nua_callstate_ready; | |||||
3990 | phrase = early ? "Early Session Terminated" : "Session Terminated"; | |||||
3991 | ||||||
3992 | #if 0 | |||||
3993 | sr->sr_usage = NULL((void*)0); | |||||
3994 | #endif | |||||
3995 | ||||||
3996 | for (sr0 = nh->nh_ds->ds_sr; sr0; sr0 = sr_next) { | |||||
3997 | sr_next = sr0->sr_next; | |||||
3998 | ||||||
3999 | if (sr == sr0 || sr0->sr_usage != sr->sr_usage) | |||||
4000 | continue; | |||||
4001 | ||||||
4002 | if (nua_server_request_is_pending(sr0)) { | |||||
4003 | SR_STATUS(sr0, 487, phrase)((sr0)->sr_phrase = (phrase), (sr0)->sr_status = (487)); | |||||
4004 | nua_server_respond(sr0, NULL((void*)0)); | |||||
4005 | } | |||||
4006 | nua_server_request_destroy(sr0); | |||||
4007 | } | |||||
4008 | ||||||
4009 | sr->sr_phrase = phrase; | |||||
4010 | } | |||||
4011 | ||||||
4012 | retval = nua_base_server_report(sr, tags); | |||||
4013 | ||||||
4014 | //assert(2 <= retval && retval < 4); | |||||
4015 | ||||||
4016 | #if 0 | |||||
4017 | if (ss) { | |||||
4018 | signal_call_state_change(nh, ss, 200, | |||||
4019 | early ? "Received early BYE" : "Received BYE", | |||||
4020 | nua_callstate_terminated); | |||||
4021 | nua_dialog_usage_remove(nh, nh->nh_ds, du); | |||||
4022 | } | |||||
4023 | #endif | |||||
4024 | ||||||
4025 | return retval; | |||||
4026 | } | |||||
4027 | ||||||
4028 | /* ---------------------------------------------------------------------- */ | |||||
4029 | ||||||
4030 | /** @NUA_EVENT nua_i_state | |||||
4031 | * | |||||
4032 | * @brief Call state has changed. | |||||
4033 | * | |||||
4034 | * This event will be sent whenever the call state changes. | |||||
4035 | * | |||||
4036 | * In addition to basic changes of session status indicated with enum | |||||
4037 | * ::nua_callstate, the @RFC3264 SDP Offer/Answer negotiation status is also | |||||
4038 | * included. The tags NUTAG_OFFER_RECV() or NUTAG_ANSWER_RECV() indicate | |||||
4039 | * whether the remote SDP that was received was considered as an offer or an | |||||
4040 | * answer. Tags NUTAG_OFFER_SENT() or NUTAG_ANSWER_SENT() indicate whether | |||||
4041 | * the local SDP which was sent was considered as an offer or answer. | |||||
4042 | * | |||||
4043 | * If the @b soa SDP negotiation is enabled (by default or with | |||||
4044 | * NUTAG_MEDIA_ENABLE(1)), the received remote SDP is included in tags | |||||
4045 | * SOATAG_REMOTE_SDP() and SOATAG_REMOTE_SDP_STR(). The SDP negotiation | |||||
4046 | * result from @b soa is included in the tags SOATAG_LOCAL_SDP() and | |||||
4047 | * SOATAG_LOCAL_SDP_STR(). | |||||
4048 | * | |||||
4049 | * SOATAG_ACTIVE_AUDIO() and SOATAG_ACTIVE_VIDEO() are informational tags | |||||
4050 | * used to indicate what is the status of audio or video. | |||||
4051 | * | |||||
4052 | * Note that #nua_i_state also covers the information relayed in call | |||||
4053 | * establisment (#nua_i_active) and termination (#nua_i_terminated) events. | |||||
4054 | * | |||||
4055 | * @param status protocol status code \n | |||||
4056 | * (always present) | |||||
4057 | * @param phrase short description of status code \n | |||||
4058 | * (always present) | |||||
4059 | * @param nh operation handle associated with the call | |||||
4060 | * @param hmagic application context associated with the call | |||||
4061 | * @param sip NULL | |||||
4062 | * @param tags NUTAG_CALLSTATE(), | |||||
4063 | * SOATAG_LOCAL_SDP(), SOATAG_LOCAL_SDP_STR(), | |||||
4064 | * NUTAG_OFFER_SENT(), NUTAG_ANSWER_SENT(), | |||||
4065 | * SOATAG_REMOTE_SDP(), SOATAG_REMOTE_SDP_STR(), | |||||
4066 | * NUTAG_OFFER_RECV(), NUTAG_ANSWER_RECV(), | |||||
4067 | * SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO(), | |||||
4068 | * SOATAG_ACTIVE_IMAGE(), SOATAG_ACTIVE_CHAT(). | |||||
4069 | * | |||||
4070 | * @sa @ref nua_call_model, #nua_i_active, #nua_i_terminated, | |||||
4071 | * nua_invite(), #nua_r_invite, #nua_i_invite, nua_respond(), | |||||
4072 | * NUTAG_MEDIA_ENABLE(), | |||||
4073 | * NUTAG_AUTOALERT(), NUTAG_AUTOANSWER(), NUTAG_EARLY_MEDIA(), | |||||
4074 | * NUTAG_EARLY_ANSWER(), NUTAG_INCLUDE_EXTRA_SDP(), | |||||
4075 | * nua_ack(), NUTAG_AUTOACK(), nua_bye(), #nua_r_bye, #nua_i_bye, | |||||
4076 | * nua_cancel(), #nua_r_cancel, #nua_i_cancel, | |||||
4077 | * nua_prack(), #nua_r_prack, #nua_i_prack, | |||||
4078 | * nua_update(), #nua_r_update, #nua_i_update | |||||
4079 | * | |||||
4080 | * @par History | |||||
4081 | * Prior @VERSION_1_12_6 the tags NUTAG_OFFER_RECV(), NUTAG_ANSWER_RECV(), | |||||
4082 | * NUTAG_ANSWER_SENT(), NUTAG_OFFER_SENT() were not included with | |||||
4083 | * nua_i_state eventif media was disabled. | |||||
4084 | * | |||||
4085 | * @END_NUA_EVENT | |||||
4086 | */ | |||||
4087 | ||||||
4088 | /** | |||||
4089 | * Delivers call state changed event to the nua client. @internal | |||||
4090 | * | |||||
4091 | * @param nh call handle | |||||
4092 | * @param status status code | |||||
4093 | * @param tr_event SIP transaction event triggering this change | |||||
4094 | * @param oa_recv Received SDP | |||||
4095 | * @param oa_sent Sent SDP | |||||
4096 | */ | |||||
4097 | ||||||
4098 | static void signal_call_state_change(nua_handle_t *nh, | |||||
4099 | nua_session_usage_t *ss, | |||||
4100 | int status, char const *phrase, | |||||
4101 | enum nua_callstate next_state) | |||||
4102 | { | |||||
4103 | enum nua_callstate ss_state = nua_callstate_init; | |||||
4104 | enum nua_callstate invite_state = next_state; | |||||
4105 | ||||||
4106 | char const *oa_recv = NULL((void*)0); | |||||
4107 | char const *oa_sent = NULL((void*)0); | |||||
4108 | ||||||
4109 | int offer_recv = 0, answer_recv = 0, offer_sent = 0, answer_sent = 0; | |||||
4110 | ||||||
4111 | if (ss) { | |||||
4112 | if (ss->ss_reporting) | |||||
4113 | return; | |||||
4114 | ||||||
4115 | ss_state = ss->ss_state; | |||||
4116 | oa_recv = ss->ss_oa_recv, ss->ss_oa_recv = NULL((void*)0); | |||||
4117 | oa_sent = ss->ss_oa_sent, ss->ss_oa_sent = NULL((void*)0); | |||||
4118 | ||||||
4119 | assert(oa_sent == Offer || oa_sent == Answer || oa_sent == NULL)((oa_sent == Offer || oa_sent == Answer || oa_sent == ((void* )0)) ? (void) (0) : __assert_fail ("oa_sent == Offer || oa_sent == Answer || oa_sent == ((void*)0)" , "nua_session.c", 4119, __PRETTY_FUNCTION__)); | |||||
4120 | assert(oa_recv == Offer || oa_recv == Answer || oa_recv == NULL)((oa_recv == Offer || oa_recv == Answer || oa_recv == ((void* )0)) ? (void) (0) : __assert_fail ("oa_recv == Offer || oa_recv == Answer || oa_recv == ((void*)0)" , "nua_session.c", 4120, __PRETTY_FUNCTION__)); | |||||
4121 | ||||||
4122 | if (oa_recv) { | |||||
4123 | offer_recv = oa_recv == Offer; | |||||
4124 | answer_recv = oa_recv == Answer; | |||||
4125 | } | |||||
4126 | ||||||
4127 | if (oa_sent) { | |||||
4128 | offer_sent = oa_sent == Offer; | |||||
4129 | answer_sent = oa_sent == Answer; | |||||
4130 | } | |||||
4131 | } | |||||
4132 | ||||||
4133 | if (ss_state < nua_callstate_ready || next_state > nua_callstate_ready) | |||||
4134 | SU_DEBUG_5(("nua(%p): call state changed: %s -> %s%s%s%s%s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||||
4135 | (void *)nh, nua_callstate_name(ss_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||||
4136 | nua_callstate_name(next_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||||
4137 | oa_recv ? ", received " : "", oa_recv ? oa_recv : "",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||||
4138 | oa_sent && oa_recv ? ", and sent " :((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0) | |||||
4139 | oa_sent ? ", sent " : "", oa_sent ? oa_sent : ""))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4139, "nua(%p): call state changed: %s -> %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(ss_state), nua_callstate_name (next_state), oa_recv ? ", received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", and sent " : oa_sent ? ", sent " : "", oa_sent ? oa_sent : "")) : (void)0); | |||||
4140 | else | |||||
4141 | SU_DEBUG_5(("nua(%p): ready call updated: %s%s%s%s%s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4145, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||||
4142 | (void *)nh, nua_callstate_name(next_state),((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4145, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||||
4143 | oa_recv ? " received " : "", oa_recv ? oa_recv : "",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4145, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||||
4144 | oa_sent && oa_recv ? ", sent " :((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4145, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0) | |||||
4145 | oa_sent ? " sent " : "", oa_sent ? oa_sent : ""))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4145, "nua(%p): ready call updated: %s%s%s%s%s\n" , (void *)nh, nua_callstate_name(next_state), oa_recv ? " received " : "", oa_recv ? oa_recv : "", oa_sent && oa_recv ? ", sent " : oa_sent ? " sent " : "", oa_sent ? oa_sent : "")) : (void) 0); | |||||
4146 | ||||||
4147 | if (next_state == nua_callstate_terminating && | |||||
4148 | ss_state >= nua_callstate_terminating) | |||||
4149 | return; | |||||
4150 | ||||||
4151 | if (ss) { | |||||
4152 | /* Update state variables */ | |||||
4153 | if (next_state == nua_callstate_init) { | |||||
4154 | if (ss_state < nua_callstate_ready) | |||||
4155 | ss->ss_state = next_state; | |||||
4156 | else if (ss->ss_state == nua_callstate_ready) | |||||
4157 | next_state = ss->ss_state; | |||||
4158 | else if (ss->ss_state == nua_callstate_terminating) | |||||
4159 | return; | |||||
4160 | else | |||||
4161 | ss->ss_state = next_state = nua_callstate_terminated; | |||||
4162 | } | |||||
4163 | else if (next_state > ss_state) | |||||
4164 | ss->ss_state = next_state; | |||||
4165 | } | |||||
4166 | ||||||
4167 | if (next_state == nua_callstate_init) | |||||
4168 | next_state = nua_callstate_terminated; | |||||
4169 | ||||||
4170 | if (ss && ss->ss_state == nua_callstate_ready) | |||||
4171 | nh->nh_active_call = 1; | |||||
4172 | else if (next_state == nua_callstate_terminated) | |||||
4173 | nh->nh_active_call = 0; | |||||
4174 | ||||||
4175 | /* Send events */ | |||||
4176 | if (phrase == NULL((void*)0)) | |||||
4177 | phrase = "Call state"; | |||||
4178 | ||||||
4179 | { | |||||
4180 | sdp_session_t const *remote_sdp = NULL((void*)0); | |||||
4181 | char const *remote_sdp_str = NULL((void*)0); | |||||
4182 | sdp_session_t const *local_sdp = NULL((void*)0); | |||||
4183 | char const *local_sdp_str = NULL((void*)0); | |||||
4184 | ||||||
4185 | if (nh->nh_soa) { | |||||
4186 | if (oa_recv) | |||||
4187 | soa_get_remote_sdp(nh->nh_soa, &remote_sdp, &remote_sdp_str, 0); | |||||
4188 | if (oa_sent) | |||||
4189 | soa_get_local_sdp(nh->nh_soa, &local_sdp, &local_sdp_str, 0); | |||||
4190 | ||||||
4191 | if (answer_recv || answer_sent) { /* Update nh_hold_remote */ | |||||
4192 | char const *held = NULL((void*)0); | |||||
4193 | soa_get_params(nh->nh_soa, SOATAG_HOLD_REF(held)soatag_hold_ref, tag_str_vr(&(held)), TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
4194 | nh->nh_hold_remote = held && strlen(held) > 0; | |||||
4195 | } | |||||
4196 | } | |||||
4197 | else | |||||
4198 | oa_recv = NULL((void*)0), oa_sent = NULL((void*)0); | |||||
4199 | ||||||
4200 | nua_stack_tevent(nh->nh_nua, nh, NULL((void*)0), nua_i_state, | |||||
4201 | status, phrase, | |||||
4202 | NUTAG_CALLSTATE(next_state)nutag_callstate, tag_int_v(next_state), | |||||
4203 | NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_audio_active(nh->nh_soa)), !((1) && (nh-> nh_soa) && soa_is_video_active(nh->nh_soa) >= 0 ) ? tag_skip : soatag_active_video, tag_int_v(soa_is_video_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_image_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_image , tag_int_v(soa_is_image_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_chat_active (nh->nh_soa)), | |||||
4204 | /* NUTAG_SOA_SESSION(nh->nh_soa), */ | |||||
4205 | TAG_IF(offer_recv, NUTAG_OFFER_RECV(offer_recv))!(offer_recv) ? tag_skip : nutag_offer_recv, tag_bool_v(offer_recv ), | |||||
4206 | TAG_IF(answer_recv, NUTAG_ANSWER_RECV(answer_recv))!(answer_recv) ? tag_skip : nutag_answer_recv, tag_bool_v(answer_recv ), | |||||
4207 | TAG_IF(offer_sent, NUTAG_OFFER_SENT(offer_sent))!(offer_sent) ? tag_skip : nutag_offer_sent, tag_bool_v(offer_sent ), | |||||
4208 | TAG_IF(answer_sent, NUTAG_ANSWER_SENT(answer_sent))!(answer_sent) ? tag_skip : nutag_answer_sent, tag_bool_v(answer_sent ), | |||||
4209 | TAG_IF(oa_recv, SOATAG_REMOTE_SDP(remote_sdp))!(oa_recv) ? tag_skip : soatag_remote_sdp, sdptag_session_v(remote_sdp ), | |||||
4210 | TAG_IF(oa_recv, SOATAG_REMOTE_SDP_STR(remote_sdp_str))!(oa_recv) ? tag_skip : soatag_remote_sdp_str, tag_str_v(remote_sdp_str ), | |||||
4211 | TAG_IF(oa_sent, SOATAG_LOCAL_SDP(local_sdp))!(oa_sent) ? tag_skip : soatag_local_sdp, sdptag_session_v(local_sdp ), | |||||
4212 | TAG_IF(oa_sent, SOATAG_LOCAL_SDP_STR(local_sdp_str))!(oa_sent) ? tag_skip : soatag_local_sdp_str, tag_str_v(local_sdp_str ), | |||||
4213 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
4214 | } | |||||
4215 | ||||||
4216 | if (next_state == nua_callstate_ready && ss_state <= nua_callstate_ready) { | |||||
4217 | nua_stack_tevent(nh->nh_nua, nh, NULL((void*)0), nua_i_active, status, "Call active", | |||||
4218 | NH_ACTIVE_MEDIA_TAGS(1, nh->nh_soa)!((1) && (nh->nh_soa) && soa_is_audio_active (nh->nh_soa) >= 0) ? tag_skip : soatag_active_audio, tag_int_v (soa_is_audio_active(nh->nh_soa)), !((1) && (nh-> nh_soa) && soa_is_video_active(nh->nh_soa) >= 0 ) ? tag_skip : soatag_active_video, tag_int_v(soa_is_video_active (nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_image_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_image , tag_int_v(soa_is_image_active(nh->nh_soa)), !((1) && (nh->nh_soa) && soa_is_chat_active(nh->nh_soa) >= 0) ? tag_skip : soatag_active_chat, tag_int_v(soa_is_chat_active (nh->nh_soa)), | |||||
4219 | /* NUTAG_SOA_SESSION(nh->nh_soa), */ | |||||
4220 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
4221 | } | |||||
4222 | ||||||
4223 | else if (next_state == nua_callstate_terminated) { | |||||
4224 | nua_stack_event(nh->nh_nua, nh, NULL((void*)0), | |||||
4225 | nua_i_terminated, status, phrase, | |||||
4226 | NULL((void*)0)); | |||||
4227 | } | |||||
4228 | ||||||
4229 | if (invite_state == nua_callstate_ready) { | |||||
4230 | /* Start next INVITE request, if queued */ | |||||
4231 | nua_client_next_request(nh->nh_ds->ds_cr, 1); | |||||
4232 | } | |||||
4233 | } | |||||
4234 | ||||||
4235 | /** @NUA_EVENT nua_i_active | |||||
4236 | * | |||||
4237 | * A call has been activated. | |||||
4238 | * | |||||
4239 | * This event will be sent after a succesful response to the initial | |||||
4240 | * INVITE has been received and the media has been activated. | |||||
4241 | * | |||||
4242 | * @param nh operation handle associated with the call | |||||
4243 | * @param hmagic application context associated with the call | |||||
4244 | * @param sip NULL | |||||
4245 | * @param tags SOATAG_ACTIVE_AUDIO(), SOATAG_ACTIVE_VIDEO(), | |||||
4246 | * SOATAG_ACTIVE_IMAGE(), SOATAG_ACTIVE_CHAT(). | |||||
4247 | * | |||||
4248 | * @deprecated Use #nua_i_state instead. | |||||
4249 | * | |||||
4250 | * @sa @ref nua_call_model, #nua_i_state, #nua_i_terminated, | |||||
4251 | * #nua_i_invite | |||||
4252 | * | |||||
4253 | * @END_NUA_EVENT | |||||
4254 | */ | |||||
4255 | ||||||
4256 | /** @NUA_EVENT nua_i_terminated | |||||
4257 | * | |||||
4258 | * A call has been terminated. | |||||
4259 | * | |||||
4260 | * This event will be sent after a call has been terminated. A call is | |||||
4261 | * terminated, when | |||||
4262 | * 1) an error response (300..599) is sent to an incoming initial INVITE | |||||
4263 | * 2) a reliable response (200..299 or reliable preliminary response) to | |||||
4264 | * an incoming initial INVITE is not acknowledged with ACK or PRACK | |||||
4265 | * 3) BYE is received or sent | |||||
4266 | * | |||||
4267 | * @param nh operation handle associated with the call | |||||
4268 | * @param hmagic application context associated with the call | |||||
4269 | * @param sip NULL | |||||
4270 | * @param tags empty | |||||
4271 | * | |||||
4272 | * @deprecated Use #nua_i_state instead. | |||||
4273 | * | |||||
4274 | * @sa @ref nua_call_model, #nua_i_state, #nua_i_active, #nua_i_bye, | |||||
4275 | * #nua_i_invite | |||||
4276 | * | |||||
4277 | * @END_NUA_EVENT | |||||
4278 | */ | |||||
4279 | ||||||
4280 | ||||||
4281 | /* ======================================================================== */ | |||||
4282 | ||||||
4283 | static | |||||
4284 | int nua_server_retry_after(nua_server_request_t *sr, | |||||
4285 | int status, char const *phrase, | |||||
4286 | int min, int max) | |||||
4287 | { | |||||
4288 | sip_retry_after_t af[1]; | |||||
4289 | ||||||
4290 | sip_retry_after_init(af); | |||||
4291 | af->af_delta = (unsigned)su_randint(min, max); | |||||
4292 | af->af_comment = phrase; | |||||
4293 | ||||||
4294 | sip_add_dup(sr->sr_response.msg, sr->sr_response.sip, (sip_header_t *)af); | |||||
4295 | ||||||
4296 | return sr_status(sr, status, phrase); | |||||
4297 | } | |||||
4298 | ||||||
4299 | /* ======================================================================== */ | |||||
4300 | /* Session timer - RFC 4028 */ | |||||
4301 | ||||||
4302 | static int session_timer_is_supported(struct session_timer const *t) | |||||
4303 | { | |||||
4304 | return t->local.supported; | |||||
4305 | } | |||||
4306 | ||||||
4307 | /** Set session timer preferences */ | |||||
4308 | static | |||||
4309 | void session_timer_preferences(struct session_timer *t, | |||||
4310 | sip_t const *sip, | |||||
4311 | sip_supported_t const *supported, | |||||
4312 | unsigned expires, | |||||
4313 | int isset, | |||||
4314 | enum nua_session_refresher refresher, | |||||
4315 | unsigned min_se) | |||||
4316 | { | |||||
4317 | memset(&t->local, 0, sizeof t->local); | |||||
4318 | ||||||
4319 | t->local.require = sip_has_feature(sip->sip_require, "timer"); | |||||
4320 | t->local.supported = | |||||
4321 | sip_has_feature(supported, "timer") || | |||||
4322 | sip_has_feature(sip->sip_supported, "timer"); | |||||
4323 | if (isset || refresher != nua_no_refresher) | |||||
4324 | t->local.expires = expires; | |||||
4325 | else | |||||
4326 | t->local.defaults = expires; | |||||
4327 | t->local.min_se = min_se; | |||||
4328 | t->local.refresher = refresher; | |||||
4329 | } | |||||
4330 | ||||||
4331 | static int session_timer_check_restart(nua_client_request_t *cr, | |||||
4332 | int status, char const *phrase, | |||||
4333 | sip_t const *sip) | |||||
4334 | { | |||||
4335 | if (status == 422) { | |||||
4336 | nua_session_usage_t *ss = nua_dialog_usage_private(cr->cr_usage)((cr->cr_usage) ? (void*)((cr->cr_usage) + 1) : ((void* )0)); | |||||
4337 | ||||||
4338 | if (ss && session_timer_is_supported(ss->ss_timer)) { | |||||
4339 | struct session_timer *t = ss->ss_timer; | |||||
4340 | ||||||
4341 | if (sip->sip_min_se && t->local.min_se < sip->sip_min_se->min_delta) | |||||
4342 | t->local.min_se = sip->sip_min_se->min_delta; | |||||
4343 | if (t->local.expires != 0 && t->local.min_se > t->local.expires) | |||||
4344 | t->local.expires = t->local.min_se; | |||||
4345 | ||||||
4346 | return nua_client_restart(cr, 100, "Re-Negotiating Session Timer"); | |||||
4347 | } | |||||
4348 | } | |||||
4349 | ||||||
4350 | return nua_base_client_check_restart(cr, status, phrase, sip); | |||||
4351 | } | |||||
4352 | ||||||
4353 | /** Check that received Session-Expires is longer than Min-SE */ | |||||
4354 | static | |||||
4355 | int session_timer_check_min_se(msg_t *msg, | |||||
4356 | sip_t *sip, | |||||
4357 | sip_t const *request, | |||||
4358 | unsigned long min) | |||||
4359 | { | |||||
4360 | if (min == 0) | |||||
4361 | min = 1; | |||||
4362 | ||||||
4363 | /* | |||||
4364 | If an incoming request contains a Supported header field with a value | |||||
4365 | 'timer' and a Session Expires header field, the UAS MAY reject the | |||||
4366 | INVITE request with a 422 (Session Interval Too Small) response if | |||||
4367 | the session interval in the Session-Expires header field is smaller | |||||
4368 | than the minimum interval defined by the UAS' local policy. When | |||||
4369 | sending the 422 response, the UAS MUST include a Min-SE header field | |||||
4370 | with the value of its minimum interval. This minimum interval MUST | |||||
4371 | NOT be lower than 90 seconds. | |||||
4372 | */ | |||||
4373 | if (request->sip_session_expires && | |||||
4374 | sip_has_feature(request->sip_supported, "timer") && | |||||
4375 | request->sip_session_expires->x_delta < min) { | |||||
4376 | sip_min_se_t min_se[1]; | |||||
4377 | ||||||
4378 | if (min < 90) | |||||
4379 | min = 90; | |||||
4380 | ||||||
4381 | sip_min_se_init(min_se)->min_delta = min; | |||||
4382 | ||||||
4383 | /* Include extension parameters, if any */ | |||||
4384 | if (request->sip_min_se) | |||||
4385 | min_se->min_params = request->sip_min_se->min_params; | |||||
4386 | ||||||
4387 | sip_add_dup(msg, sip, (sip_header_t *)min_se); | |||||
4388 | ||||||
4389 | return 422; | |||||
4390 | } | |||||
4391 | ||||||
4392 | return 0; | |||||
4393 | } | |||||
4394 | ||||||
4395 | /** Store session timer parameters in request from uac / response from uas */ | |||||
4396 | static | |||||
4397 | void session_timer_store(struct session_timer *t, | |||||
4398 | sip_t const *sip) | |||||
4399 | { | |||||
4400 | sip_require_t const *require = sip->sip_require; | |||||
4401 | sip_supported_t const *supported = sip->sip_supported; | |||||
4402 | sip_session_expires_t const *x = sip->sip_session_expires; | |||||
4403 | ||||||
4404 | t->remote.require = require && sip_has_feature(require, "timer"); | |||||
4405 | t->remote.supported = | |||||
4406 | t->remote.supported || (supported && sip_has_feature(supported, "timer")); | |||||
4407 | ||||||
4408 | t->remote.expires = 0; | |||||
4409 | t->remote.refresher = nua_any_refresher; | |||||
4410 | t->remote.min_se = 0; | |||||
4411 | ||||||
4412 | if (x) { | |||||
4413 | t->remote.expires = x->x_delta; | |||||
4414 | ||||||
4415 | if (x->x_refresher) { | |||||
4416 | int uas = sip->sip_request != NULL((void*)0); | |||||
4417 | ||||||
4418 | if (su_casenmatch(x->x_refresher, "uac", (sizeof "uac"))) | |||||
4419 | t->remote.refresher = uas ? nua_remote_refresher : nua_local_refresher; | |||||
4420 | else if (su_casenmatch(x->x_refresher, "uas", (sizeof "uas"))) | |||||
4421 | t->remote.refresher = uas ? nua_local_refresher : nua_remote_refresher; | |||||
4422 | } | |||||
4423 | else if (t->remote.require) { | |||||
4424 | /* Require: timer but no refresher parameter in Session-Expires header */ | |||||
4425 | t->remote.refresher = nua_local_refresher; | |||||
4426 | } | |||||
4427 | } | |||||
4428 | ||||||
4429 | if (sip->sip_min_se) | |||||
4430 | t->remote.min_se = sip->sip_min_se->min_delta; | |||||
4431 | } | |||||
4432 | ||||||
4433 | /** Add timer feature and Session-Expires/Min-SE headers to request/response | |||||
4434 | * | |||||
4435 | */ | |||||
4436 | static int | |||||
4437 | session_timer_add_headers(struct session_timer *t, | |||||
4438 | int initial, | |||||
4439 | msg_t *msg, | |||||
4440 | sip_t *sip, | |||||
4441 | nua_handle_t *nh) | |||||
4442 | { | |||||
4443 | unsigned long expires, min; | |||||
4444 | sip_min_se_t min_se[1]; | |||||
4445 | sip_session_expires_t x[1]; | |||||
4446 | int uas; | |||||
4447 | int autorequire = 1; | |||||
4448 | ||||||
4449 | enum nua_session_refresher refresher = nua_any_refresher; | |||||
4450 | ||||||
4451 | static sip_param_t const x_params_uac[] = {"refresher=uac", NULL((void*)0)}; | |||||
4452 | static sip_param_t const x_params_uas[] = {"refresher=uas", NULL((void*)0)}; | |||||
4453 | ||||||
4454 | if ( !NH_PGET(nh, timer_autorequire)(((nh)->nh_prefs)->nhp_set_.set_bits.nhb_timer_autorequire ? ((nh)->nh_prefs)->nhp_timer_autorequire : ((nh)-> nh_nua->nua_handles->nh_prefs)->nhp_timer_autorequire ) && NH_PISSET(nh, timer_autorequire)((((nh)->nh_prefs)->nhp_set_.set_bits.nhb_timer_autorequire ) && (nh)->nh_nua->nua_handles->nh_prefs != ( nh)->nh_prefs)) { | |||||
4455 | autorequire = 0; | |||||
4456 | } | |||||
4457 | ||||||
4458 | if (!t->local.supported) | |||||
4459 | return 0; | |||||
4460 | ||||||
4461 | uas = sip->sip_status != NULL((void*)0); | |||||
4462 | ||||||
4463 | min = t->local.min_se; | |||||
4464 | if (min < t->remote.min_se) | |||||
4465 | min = t->remote.min_se; | |||||
4466 | ||||||
4467 | if (uas) { | |||||
4468 | session_timer_negotiate(t, uas = 1); | |||||
4469 | ||||||
4470 | refresher = t->refresher; | |||||
4471 | expires = t->interval; | |||||
4472 | } | |||||
4473 | else { | |||||
4474 | /* RFC 4028: | |||||
4475 | * The UAC MAY include the refresher parameter with value 'uac' if it | |||||
4476 | * wants to perform the refreshes. However, it is RECOMMENDED that the | |||||
4477 | * parameter be omitted so that it can be selected by the negotiation | |||||
4478 | * mechanisms described below. | |||||
4479 | */ | |||||
4480 | if (t->local.refresher == nua_local_refresher) | |||||
4481 | refresher = nua_local_refresher; | |||||
4482 | else if (!initial) | |||||
4483 | refresher = t->refresher; | |||||
4484 | ||||||
4485 | expires = t->local.expires; | |||||
4486 | if (expires != 0 && expires < min) | |||||
4487 | expires = min; | |||||
4488 | ||||||
4489 | if (expires == 0 && !initial && t->interval) | |||||
4490 | expires = t->interval; | |||||
4491 | } | |||||
4492 | ||||||
4493 | sip_min_se_init(min_se)->min_delta = min; | |||||
4494 | ||||||
4495 | sip_session_expires_init(x)->x_delta = expires; | |||||
4496 | if (refresher == nua_remote_refresher) | |||||
4497 | x->x_params = uas ? x_params_uac : x_params_uas; | |||||
4498 | else if (refresher == nua_local_refresher) | |||||
4499 | x->x_params = uas ? x_params_uas : x_params_uac; | |||||
4500 | ||||||
4501 | if (expires == 0 && t->remote.min_se == 0) | |||||
4502 | /* Session timer is not used, do not add headers */ | |||||
4503 | return 1; | |||||
4504 | ||||||
4505 | sip_add_tl(msg, sip, | |||||
4506 | TAG_IF(expires != 0, SIPTAG_SESSION_EXPIRES(x))!(expires != 0) ? tag_skip : siptag_session_expires, siptag_session_expires_v (x), | |||||
4507 | TAG_IF((!uas || sip->sip_status->st_status == 422) && (min != 0!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||||
4508 | /* Min-SE: 0 is optional with initial INVITE */!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||||
4509 | || !initial),!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se) | |||||
4510 | SIPTAG_MIN_SE(min_se))!((!uas || sip->sip_status->st_status == 422) && (min != 0 || !initial)) ? tag_skip : siptag_min_se, siptag_min_se_v (min_se), | |||||
4511 | TAG_IF(autorequire && refresher == nua_remote_refresher && expires != 0, SIPTAG_REQUIRE_STR("timer"))!(autorequire && refresher == nua_remote_refresher && expires != 0) ? tag_skip : siptag_require_str, tag_str_v("timer" ), | |||||
4512 | TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
4513 | ||||||
4514 | return 1; | |||||
4515 | } | |||||
4516 | ||||||
4517 | static void | |||||
4518 | session_timer_negotiate(struct session_timer *t, int uas) | |||||
4519 | { | |||||
4520 | if (!t->local.supported) | |||||
4521 | t->refresher = nua_no_refresher; | |||||
4522 | else if (!t->remote.supported) | |||||
4523 | t->refresher = nua_local_refresher; | |||||
4524 | else if (t->remote.refresher == nua_local_refresher) | |||||
4525 | t->refresher = nua_local_refresher; | |||||
4526 | else if (t->remote.refresher == nua_remote_refresher) | |||||
4527 | t->refresher = nua_remote_refresher; | |||||
4528 | else if (uas) | |||||
4529 | /* UAS defaults UAC to refreshing */ | |||||
4530 | t->refresher = nua_remote_refresher; | |||||
4531 | else | |||||
4532 | /* UAC refreshes by itself */ | |||||
4533 | t->refresher = nua_local_refresher; | |||||
4534 | ||||||
4535 | t->interval = t->remote.expires; | |||||
4536 | if (t->interval == 0) | |||||
4537 | t->interval = t->local.expires; | |||||
4538 | if (t->local.expires != 0 && t->interval > t->local.expires) | |||||
4539 | t->interval = t->local.expires; | |||||
4540 | if (t->local.defaults != 0 && t->interval > t->local.defaults) | |||||
4541 | t->interval = t->local.defaults; | |||||
4542 | ||||||
4543 | if (t->interval != 0) { | |||||
4544 | if (t->interval < t->local.min_se) | |||||
4545 | t->interval = t->local.min_se; | |||||
4546 | if (t->interval < t->remote.min_se) | |||||
4547 | t->interval = t->remote.min_se; | |||||
4548 | } | |||||
4549 | ||||||
4550 | if (t->interval == 0) | |||||
4551 | t->refresher = nua_no_refresher; | |||||
4552 | } | |||||
4553 | ||||||
4554 | static void | |||||
4555 | session_timer_set(nua_session_usage_t *ss, int uas) | |||||
4556 | { | |||||
4557 | nua_dialog_usage_t *du = nua_dialog_usage_public(ss)((ss) ? (nua_dialog_usage_t*)(ss) - 1 : ((void*)0)); | |||||
4558 | struct session_timer *t; | |||||
4559 | ||||||
4560 | if (ss == NULL((void*)0)) | |||||
4561 | return; | |||||
4562 | ||||||
4563 | t = ss->ss_timer; | |||||
4564 | ||||||
4565 | session_timer_negotiate(t, uas); | |||||
4566 | ||||||
4567 | if (t->refresher == nua_local_refresher) { | |||||
4568 | unsigned low = t->interval / 2, high = t->interval / 2; | |||||
4569 | ||||||
4570 | if (t->interval >= 90) | |||||
4571 | low -=5, high += 5; | |||||
4572 | ||||||
4573 | nua_dialog_usage_set_refresh_range(du, low, high); | |||||
4574 | t->timer_set = 1; | |||||
4575 | } | |||||
4576 | else if (t->refresher == nua_remote_refresher) { | |||||
4577 | /* RFC 4028 10.3 and 10.4: Send BYE before the session expires. | |||||
4578 | Increased interval from 2/3 to 9/10 of session expiration delay | |||||
4579 | because some endpoints won't UPDATE early enough with very short | |||||
4580 | sessions (e.g. 120). */ | |||||
4581 | ||||||
4582 | unsigned interval = t->interval; | |||||
4583 | ||||||
4584 | interval -= 32 > interval / 10 ? interval / 10 : 32; | |||||
4585 | ||||||
4586 | nua_dialog_usage_set_refresh_range(du, interval, interval); | |||||
4587 | t->timer_set = 1; | |||||
4588 | } | |||||
4589 | else { | |||||
4590 | nua_dialog_usage_reset_refresh(du); | |||||
4591 | t->timer_set = 0; | |||||
4592 | } | |||||
4593 | } | |||||
4594 | ||||||
4595 | /* ======================================================================== */ | |||||
4596 | ||||||
4597 | /** Get SDP from a SIP message. | |||||
4598 | * | |||||
4599 | * @retval 1 if message contains SDP | |||||
4600 | * @retval 0 if message does not contain valid SDP | |||||
4601 | */ | |||||
4602 | static | |||||
4603 | int session_get_description(sip_t const *sip, | |||||
4604 | char const **return_sdp, | |||||
4605 | size_t *return_len) | |||||
4606 | { | |||||
4607 | sip_payload_t const *pl = sip->sip_payload; | |||||
4608 | sip_content_type_t const *ct = sip->sip_content_type; | |||||
4609 | int matching_content_type = 0; | |||||
4610 | ||||||
4611 | if (pl == NULL((void*)0)) | |||||
4612 | return 0; | |||||
4613 | else if (pl->pl_len == 0 || pl->pl_data == NULL((void*)0)) | |||||
4614 | return 0; | |||||
4615 | else if (ct == NULL((void*)0)) | |||||
4616 | /* Be bug-compatible with our old gateways */ | |||||
4617 | SU_DEBUG_3(("nua: no %s, assuming %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4618, "nua: no %s, assuming %s\n", "Content-Type" , nua_application_sdp)) : (void)0) | |||||
4618 | "Content-Type", SDP_MIME_TYPE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4618, "nua: no %s, assuming %s\n", "Content-Type" , nua_application_sdp)) : (void)0); | |||||
4619 | else if (ct->c_type == NULL((void*)0)) | |||||
4620 | SU_DEBUG_3(("nua: empty %s, assuming %s\n",((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4621, "nua: empty %s, assuming %s\n" , "Content-Type", nua_application_sdp)) : (void)0) | |||||
4621 | "Content-Type", SDP_MIME_TYPE))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 3 ? (_su_llog((nua_log), 3, "nua_session.c" , (const char *)__func__, 4621, "nua: empty %s, assuming %s\n" , "Content-Type", nua_application_sdp)) : (void)0); | |||||
4622 | else if (!su_casematch(ct->c_type, SDP_MIME_TYPEnua_application_sdp)) { | |||||
4623 | SU_DEBUG_5(("nua: unknown %s: %s\n", "Content-Type", ct->c_type))((((nua_log) != ((void*)0) && (nua_log)->log_init) == 0 ? 9 : (((nua_log) != ((void*)0) && (nua_log)-> log_init > 1) ? (nua_log)->log_level : su_log_default-> log_level)) >= 5 ? (_su_llog((nua_log), 5, "nua_session.c" , (const char *)__func__, 4623, "nua: unknown %s: %s\n", "Content-Type" , ct->c_type)) : (void)0); | |||||
4624 | return 0; | |||||
4625 | } | |||||
4626 | else | |||||
4627 | matching_content_type = 1; | |||||
4628 | ||||||
4629 | if (pl == NULL((void*)0)) | |||||
4630 | return 0; | |||||
4631 | ||||||
4632 | if (!matching_content_type) { | |||||
4633 | /* Make sure we got SDP */ | |||||
4634 | if (pl->pl_len < 3 || !su_casenmatch(pl->pl_data, "v=0", 3)) | |||||
4635 | return 0; | |||||
4636 | } | |||||
4637 | ||||||
4638 | if (return_sdp && return_len) { | |||||
4639 | *return_sdp = pl->pl_data; | |||||
4640 | *return_len = pl->pl_len; | |||||
4641 | } | |||||
4642 | ||||||
4643 | return 1; | |||||
4644 | } | |||||
4645 | ||||||
4646 | /** Insert SDP into SIP message */ | |||||
4647 | static | |||||
4648 | int session_include_description(soa_session_t *soa, | |||||
4649 | int session, | |||||
4650 | msg_t *msg, | |||||
4651 | sip_t *sip) | |||||
4652 | { | |||||
4653 | su_home_t *home = msg_home(msg)((su_home_t*)(msg)); | |||||
4654 | ||||||
4655 | sip_content_disposition_t *cd = NULL((void*)0); | |||||
4656 | sip_content_type_t *ct = NULL((void*)0); | |||||
4657 | sip_payload_t *pl = NULL((void*)0); | |||||
4658 | ||||||
4659 | int retval; | |||||
4660 | ||||||
4661 | if (!soa) | |||||
4662 | return 0; | |||||
4663 | ||||||
4664 | retval = session_make_description(home, soa, session, &cd, &ct, &pl); | |||||
4665 | ||||||
4666 | if (retval <= 0) | |||||
4667 | return retval; | |||||
4668 | ||||||
4669 | if ((cd && sip_header_insert(msg, sip, (sip_header_t *)cd) < 0) || | |||||
4670 | sip_header_insert(msg, sip, (sip_header_t *)ct) < 0 || | |||||
4671 | sip_header_insert(msg, sip, (sip_header_t *)pl) < 0) | |||||
4672 | return -1; | |||||
4673 | ||||||
4674 | return retval; | |||||
4675 | } | |||||
4676 | ||||||
4677 | /** Generate SDP headers */ | |||||
4678 | static | |||||
4679 | int session_make_description(su_home_t *home, | |||||
4680 | soa_session_t *soa, | |||||
4681 | int session, | |||||
4682 | sip_content_disposition_t **return_cd, | |||||
4683 | sip_content_type_t **return_ct, | |||||
4684 | sip_payload_t **return_pl) | |||||
4685 | { | |||||
4686 | char const *sdp; | |||||
4687 | isize_t len; | |||||
4688 | int retval; | |||||
4689 | ||||||
4690 | if (!soa) | |||||
4691 | return 0; | |||||
4692 | ||||||
4693 | if (session) | |||||
4694 | retval = soa_get_local_sdp(soa, 0, &sdp, &len); | |||||
4695 | else | |||||
4696 | retval = soa_get_capability_sdp(soa, 0, &sdp, &len); | |||||
4697 | ||||||
4698 | if (retval > 0) { | |||||
4699 | *return_pl = sip_payload_create(home, sdp, len); | |||||
4700 | *return_ct = sip_content_type_make(home, SDP_MIME_TYPEnua_application_sdp); | |||||
4701 | if (session) | |||||
4702 | *return_cd = sip_content_disposition_make(home, "session"); | |||||
4703 | else | |||||
4704 | *return_cd = NULL((void*)0); | |||||
4705 | ||||||
4706 | if (!*return_pl || !*return_ct) | |||||
4707 | return -1; | |||||
4708 | ||||||
4709 | if (session && !*return_cd) | |||||
4710 | return -1; | |||||
4711 | } | |||||
4712 | ||||||
4713 | return retval; | |||||
4714 | } | |||||
4715 | ||||||
4716 | /* ====================================================================== */ | |||||
4717 | ||||||
4718 | /** @NUA_EVENT nua_i_options | |||||
4719 | * | |||||
4720 | * Incoming OPTIONS request. The user-agent should respond to an OPTIONS | |||||
4721 | * request with the same statuscode as it would respond to an INVITE | |||||
4722 | * request. | |||||
4723 | * | |||||
4724 | * Stack responds automatically to OPTIONS request unless OPTIONS is | |||||
4725 | * included in the set of application methods, set by NUTAG_APPL_METHOD(). | |||||
4726 | * | |||||
4727 | * The OPTIONS request does not create a dialog. Currently the processing | |||||
4728 | * of incoming OPTIONS creates a new handle for each incoming request which | |||||
4729 | * is not assiciated with an existing dialog. If the handle @a nh is not | |||||
4730 | * bound, you should probably destroy it after responding to the OPTIONS | |||||
4731 | * request. | |||||
4732 | * | |||||
4733 | * @param status status code of response sent automatically by stack | |||||
4734 | * @param phrase a short textual description of @a status code | |||||
4735 | * @param nh operation handle associated with the OPTIONS request | |||||
4736 | * @param hmagic application context associated with the call | |||||
4737 | * (NULL if outside session) | |||||
4738 | * @param sip incoming OPTIONS request | |||||
4739 | * @param tags empty | |||||
4740 | * | |||||
4741 | * @sa nua_respond(), nua_options(), #nua_r_options, @RFC3261 section 11.2 | |||||
4742 | * | |||||
4743 | * @END_NUA_EVENT | |||||
4744 | */ | |||||
4745 | ||||||
4746 | int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags); | |||||
4747 | ||||||
4748 | nua_server_methods_t const nua_options_server_methods = | |||||
4749 | { | |||||
4750 | SIP_METHOD_OPTIONSsip_method_options, "OPTIONS", | |||||
4751 | nua_i_options, /* Event */ | |||||
4752 | { | |||||
4753 | 0, /* Do not create dialog */ | |||||
4754 | 0, /* Initial request */ | |||||
4755 | 0, /* Not a target refresh request */ | |||||
4756 | 1, /* Add Contact */ | |||||
4757 | }, | |||||
4758 | nua_base_server_init((void*)0), | |||||
4759 | nua_base_server_preprocess((void*)0), | |||||
4760 | nua_base_server_params((void*)0), | |||||
4761 | nua_options_server_respond, | |||||
4762 | nua_base_server_report, | |||||
4763 | }; | |||||
4764 | ||||||
4765 | /** @internal Respond to an OPTIONS request. | |||||
4766 | * | |||||
4767 | */ | |||||
4768 | int nua_options_server_respond(nua_server_request_t *sr, tagi_t const *tags) | |||||
4769 | { | |||||
4770 | nua_handle_t *nh = sr->sr_owner; | |||||
4771 | nua_t *nua = nh->nh_nua; | |||||
4772 | ||||||
4773 | if (200 <= sr->sr_status && sr->sr_status < 300) { | |||||
4774 | msg_t *msg = sr->sr_response.msg; | |||||
4775 | sip_t *sip = sr->sr_response.sip; | |||||
4776 | ||||||
4777 | sip_add_tl(msg, sip, SIPTAG_ACCEPT(nua->nua_invite_accept)siptag_accept, siptag_accept_v(nua->nua_invite_accept), TAG_END()(tag_type_t)0, (tag_value_t)0); | |||||
4778 | ||||||
4779 | if (!sip->sip_payload) { /* XXX - do MIME multipart? */ | |||||
4780 | soa_session_t *soa = nh->nh_soa; | |||||
4781 | ||||||
4782 | if (soa == NULL((void*)0)) | |||||
4783 | soa = nua->nua_dhandlenua_handles->nh_soa; | |||||
4784 | ||||||
4785 | session_include_description(soa, 0, msg, sip); | |||||
4786 | } | |||||
4787 | } | |||||
4788 | ||||||
4789 | return nua_base_server_respond(sr, tags); | |||||
4790 | } |