File: | libs/sofia-sip/libsofia-sip-ua/nua/nua_session.c |
Location: | line 2694, column 20 |
Description: | Access to field 'ss_reporting' results in a dereference of a null pointer (loaded from variable 'ss') |
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 | } |